Announcement

Collapse
No announcement yet.

Virtuell oder nicht virtuell

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Virtuell oder nicht virtuell

    Ich hab da ein Verständisproblem (oder ein Compilerproblem) mit virtuellen Funktionen.

    Unteres Beispiel definiert 2 Klassen mit einer virtuellen Methode SetString. Diese setzt jeweils den Member m_Text mit einem festen Text.
    Im Konstruktor der Basisklasse wird diese virtuelle Methode aufgerufen um m_Text zu Initialisieren.

    Ich würde erwarten das das Beispielprogramm die Ausgabe:

    KlasseAbleitung<br>
    KlasseAbleitung<br>
    Taste

    liefern würde.

    Jedoch bekomme ich (Mit Visual C++6.0 mit SP)

    KlasseBasis<br>
    KlasseAbleitung<br>
    Taste

    D.h. im Konstruktor der Basisklasse sind die überladenen Funktionen scheinbar noch nicht vorhanden (C++-Definition). Ist dies so definiert oder evtl. nur ein Bug vom Visual Studio?

    Danke für Erklärungen

    Bernhard

    <pre>
    #include "stdafx.h"
    #include "string.h"
    #include "conio.h"

    class KlasseBasis
    {
    public:
    char m_Text[500];
    KlasseBasis();
    virtual void SetString();
    };

    KlasseBasis::KlasseBasis()
    {
    SetString();
    }

    void KlasseBasis::SetString()
    {
    strcpy( m_Text, "KlasseBasis");
    }

    class KlasseAbleitung: public KlasseBasis
    {
    public:
    virtual void SetString();
    };

    void KlasseAbleitung::SetString()
    {
    strcpy( m_Text, "KlasseAbleitung");
    }

    int main(int argc, char* argv[])
    {
    KlasseBasis* Klasse = new KlasseAbleitung;
    printf(Klasse->m_Text);
    printf("\n");
    Klasse->SetString();
    printf(Klasse->m_Text);
    printf("\n");
    printf("Taste");
    getch();
    return 0;
    }
    </pre>

  • #2
    Das ist C++-Standard. Anderes Verhalten wäre auch gefährlich.
    Ein Beispiel: Ändere KlasseAbleitung wie folgt ab:
    <pre class="sourcecode"><code>
    <b>class</b> KlasseAbleitung: <b>public</b> KlasseBasis
    {
    <b>public</b>:
    std::string m_string;
    <b>virtual</b> <b>void</b> SetString();
    };
    <br>
    <b>void</b> KlasseAbleitung::SetString()
    {
    strcpy( m_Text, <font color="#9933CC">&quot;KlasseAbleitung&quot;</font>);
    m_string = <font color="#9933CC">&quot;KlasseAbleitung&quot;</font>;
    }
    <br>
    </code></pre>
    Wenn du nun ein Objekt von Typ KlasseAbleitung erzeugst, werden die
    Konstruktoren in folgender Reihenfolge aufgerufen:
    <ol>
    <li>KlasseBasis::KlasseBasis
    <li>std::string::string (für das eingebettete Objekt m_string)
    <li>KlasseAbleitung::KlasseAbleitung
    </ol>
    Wenn nun im Aufruf von KlasseBasis::KlasseBasis KlasseAbleitung::SetString
    (und <b>nicht</b> KlasseBasis::SetString) aufgerufen werden würde, würde
    es eine Zuweisung an das noch gar nicht konstruierte Objekt m_string
    vornehmen -- kawumm.
    <p>Um solche Fallstricke zu umgehen, ist in C++ sichergestellt, dass
    während der Ausführung eines Konstruktors das zu konstruierende Objekt
    auch vom entsprechenden Typ ist -- d.h. der vptr zeigt auf die vtable
    des statischen, nicht des dynamischen Typs (oder so ähnlich :-)).
    <p>Ciao, Uli

    Comment


    • #3
      Du kannst das das "gefährliche" Verhalten simulieren, indem du
      KlasseBasis::SetString ersetzt:
      <pre class="sourcecode"><code>
      <b>void</b> KlasseBasis::SetString()
      {
      strcpy(m_Text, <font color="#9933CC">&quot;KlasseBasis&quot;</font>);
      <b>static_cast</b>&lt;KlasseAbleitung*&gt;(<b>this</b>)-&gt;m_string = <font color="#9933CC">&quot;KlasseAbleitung&quot;</font>;
      }
      </code></pre>
      Mit typeid kannst du das Verhalten sichtbar machen:
      <pre class="sourcecode"><code>
      KlasseBasis::KlasseBasis()
      {
      printf(<font color="#9933CC">&quot;In KlasseBasis: %s\n&quot;</font>, <b>typeid</b>(*<b>this</b>).name());
      SetString();
      }
      <br>
      KlasseAbleitung::KlasseAbleitung()
      {
      printf(<font color="#9933CC">&quot;In KlasseAbleitung: %s\n&quot;</font>, <b>typeid</b>(*<b>this</b>).name());
      }
      </code></pre>
      Uli

      Comment


      • #4
        Hi !!

        ich weiß ja nicht für was du das brauchst aber ich will dir des mal erklären !! also durch da du mit dem befehl
        KlasseBasis* Klasse = new KlasseAbleitung;
        richtest du ein neuen zeiger auf KlasseBasis mit der adresse der KlasseAbleitung aber durch den aufruf fürt er automatisch den Konstruktor der KlasseBasis auf
        public:
        char m_Text[500];
        KlasseBasis(); &lt------------der den SetString der BasisKlasse ausführt
        virtual void SetString();
        };

        deswegen dein ergebnis
        erst später wenn Klasse-&gtSetString(); zum zuge kommt setzt führt er den SetString in der Abgeleiteten Klasse aus

        jetzt zur Lösung es ist kein Bug und es hat auch nichts mit deiner virtual methode zu tun das hast du richtig gemacht !!
        wenn du einfach mal den Befehl Klasse-&gtSetString(); gleich unter

        KlasseBasis* Klasse = new KlasseAbleitung;

        verschiebst hast du das ergebnis .......aber denk dran der erste m_text ist gleich KlasseBasis da der Konstruktor gleich am anfang den SetString der basis version ausführt.......also wenn du das machst dann gehts !! wenn du noch fragen hast ich beantworte gern !

        Comment


        • #5
          Wie ich das umschiffen kann ist schon klar, es hat mich nur gewundert, das es so ist.
          Ursprünglich ist mir das Verhalten bei einer Basisklasse für dynamische Arrays aufgefallen (aus diversen Gründen wollte ich das selbst machen). Die Klasse definiert eine abstrakte Methode,welche die Elementgröße zurückliefert.

          Bsp.:

          <pre>
          class CMyArray
          {
          public:
          CMyArray();
          void SetSize( int NewSize);
          virtual int GetElementSize() = 0;
          };

          class CPointArray: public CMyArray
          {
          virtual int GetElementSize();
          };

          CMyArray::CMyArray()
          {
          SetSize( 0);
          }

          void CMyArray::SetSize( int NewSize)
          {
          int NewArraySize = NewSize * GetElementSize();
          // Array mit der NewArraySize anlegen
          //.....
          }

          int CPointArray::GetElementSize()
          {
          return sizeof( POINT);
          }

          die Codezeile

          CPointArray Points;

          Generiert dann einen Runtimeerror ‚pure virtual call’, weil im Konstruktor die nicht voehandene Funktion GetElementSize der Klasse CmyArray aufgerufen wird.
          Bei Delphi und Java und auch in der neuesten Sprache C# funktionieren solche Konstrukte problemlos. Und da es in C# funktioniert muss es auch in allen anderen .NET-Sprachen funktionieren

          Comment


          • #6
            na gut dann willst du wahrscheinlich nur wissen wieso das so ist !
            aber da bin ich überfragt ...denn mit Delphi hab ich nix am hut und C#
            ist ja wieder nur ne verbesserung und erweiterung von C++ mit der ich aber noch nicht angefangen habe kann mir aber nicht vorstellen das des da geht den wo noch nichts ist kann man auch nichts aufrufen .....aber laße mich gerne eines besseren belehren
            .....

            Comment


            • #7
              Ich denke mal hier ist mal wieder eine Stolperfalle in C++ eingebaut.

              Wird eine abstrakte Methode im Konstruktor aufgerufen meckert der Compiler. Wird diese Methode in einer anderen Methode aufgerufen, welche ihrerseits im Konstruktor aufgerufen wird so gibt es keine Warnung, sondern nur einen Crach beim Starten.

              Hätte C++ wie viele anderen modernen Programmiersprachen das Objekt komplett konstruiert (Speicher + Methodentabellen), so würde es gehen, aber scheinbar ist während des Aufrufs des Konstruktors der Basisklasse das Objekt nur bis zu dieser Stufe erstellt.

              Also das C# "nur" eine Erweiterung von C++ ist, kann man so nicht sagen. Es ist aufgrund der vielen "klauens" aus anderen Sprachen (vor allem Java und Delphi) was Spracheelement betrifft eher 'ne komplett neue Sprache als 'ne Erweiterung von C++ (auch wenn der Name das suggeriert).<br>
              Es wird kein C++-Programm ohne komplette Überarbeitung des Codes als C#-Programm kompiliert werden können. Und das sollte ja eine erweiterte Version einer Programmiersprache können

              Comment


              • #8
                das stimmt aber nicht ganz was du sagst der größte teil stammt von C/C++
                Herkunft
                Die Sprache C# stammt größtenteils von C beziehungsweise C++ ab, enthält allerdings auch Anlehnungen an Java. Dabei hat der Erfinder und Entwickler von C# (Microsoft) versucht, die Sprache so einfach wie möglich und damit weniger anfällig für Fehler zu machen. An C# hat maßgeblich auch der Entwickler der Sprache Delphi, Anders Hejlsberg, mitgearbeitet.

                aber ist ja eigentlich auch würscht hauptsache ist das sie nicht so
                anfällig ist und einfache

                Comment


                • #9
                  Viele Möglichkeiten und Fehlerquellen sind von C++ nicht übernommen worden.

                  - Keine Mehrfachvererbung von mehreren Basisklassen<br>
                  - (bisher) keine Template-Klassen<br>

                  Dagegen sieht ein Code viel mehr nach Java als nach C++ aus (Aber Java hat ja auch viel von anderen Sprachen "geklaut" unter anderen auch von C++).

                  Als ich das obige Beispiel von C++ nach C# umgestellt habe so waren so ziemlich die einzigen Überbleibsel die geschweiften Klammern. Alles andere mußte so ziemlich verändert werden.

                  Statt char m_Text[500] konnte eine richtige String-Klasse verwendet werden (hat Java und Delphi auch zu bieten aber soviel ich weiß Ansi-C++ nicht). Statt printf wurde Console.WriteLn genommen (hört sich verdammt nach dem Pascal WriteLn an). Alle Methoden werden bei ihrer Definition implementiert (ist von Java). Statt include wird using verwendet (Hört sich wie uses von Delphi an). Und was bleibt als alleiniges von C++ übernommenes Merkmal übrig?

                  Aber was soll der Streit. An C#/.NET ist meines erachtens das beste das damit für uns Windows-Entwicklern das leitvolle MFC/C++-Duo in denn nächsten Jahren in der Versenkung verschindet und nach eigenen Erfahrungen auch dort hingehört

                  Comment


                  • #10
                    MFC/C++-Duo

                    Schau dir doch mal den C++Builder an.

                    Die MFC ist eigentlich eine bessere Wrapper Klasse für die WIN 32 API. Borland hat mit seiner VCL eine richtige Klassenbibliothek geschaffen, die wirklich RAD-Entwicklung unterstützt
                    Christian

                    Comment


                    • #11
                      ja des stimmt ein hoch auf BORLAND !!! :-)
                      aber C# ist bestimmt auch noch nicht das ende ,kommen bestimmt noch andere sprachen irgendjemand muß ja immer was neues erfinden :-

                      Comment


                      • #12
                        Hallo Christian.

                        Ich selbst hatte "nur" 1 Jahr mit MFC/C++ mich herumzuschlagen und konnte glücklicherweise nach Delphi wechseln. Wenn man zuvor mit den unzulänglichkeiten von C++ und/oder MFC sich herumzuschlagen hatte ist es eine Wohltat sich mit Delphi auf das zu lösende Problem konzentrieren zu können statt die Fehler/Ungereimtheiten in der Entwicklungsumgebung.<br>
                        Delphi ist zwar auch nicht fehlerfrei aber um Welten besser.<br>

                        Hallo MisterX,<br>
                        Wenn man so die Liste der Sprachen anschaut kommt ja so ziemlich alle paar Tage eine neue Sprache auf dem Markt welche diese oder jenene Vorteil bietet. :-

                        Comment


                        • #13
                          Hi Bernhard,<br>
                          <i>Statt char m_Text[500] konnte eine richtige String-Klasse verwendet werden (hat Java und Delphi
                          auch zu bieten aber soviel ich weiß Ansi-C++ nicht).</i><br>
                          Na dann guck dir noch mal "KlasseAbleitung" in Posting #2 an.
                          <p>
                          <i>Statt include wird using verwendet (Hört sich wie uses von Delphi an).</i><br>
                          Nö, hört sich wie "using" von C++ an. Wie z.B. in<tt>using std::string;</tt>. ;-)
                          <p>
                          <i>Und was bleibt als alleiniges von C++ übernommenes Merkmal übrig?</i><br>
                          Außer "using" z.B. ":" statt "extends", "namespace" statt "package".
                          Mehr fällt mir auf die Schnelle auch nicht ein.
                          <p>Ciao, Uli

                          Comment


                          • #14
                            Hallo Bernhard!

                            "unzulänglichkeiten von C++"

                            Das würde ich so nicht unterschreiben. Sicherlich ist die Programmierung in reinem C/C++ entwas mühsam, besonders im Hinblick auf bsp. Strings. Aber schließlich gibt es ja Alternativen zur MFC. Man braucht sich nur einschlägige MFC-Foren anzusehen und lächelt als Delphi / C++Builder Benutzer sanft (Delphi und C++Builder benutzen die gleich Klassenblibliothek (VCL)).

                            Ich frage mich nur, warum soviel die MFC benutzen. Gut ich habe vor Jahren auch mit der Windowsprogrammierung und der WIN 32 API angefangen. Das war mühsam. Jezt will ich komfortabel entwickeln.

                            Ich will aber hier keine Diskussion "Welches ist die beste Progamiersprache" vom Zaum brechen. Das ist sowieso nur etwas für Anfänger. Die Wahl des Mittels hängt schließlich irgendwo von der Aufgabenstellung ab. Als überzeugter Schraubenschlüssel-Benutzer sitze ich ganz schön im Mist, wenn eine Zange gebraucht wird. Für Internetanwendungen z.B. ist für mich je nach Größe und Anwenderforderungen immer noch PERL oder JAVA erste Wahl
                            Christian

                            Comment

                            Working...
                            X