Virtuelle C++-Funktion vom Konstruktor [duplicate]

Lesezeit: 7 Minuten

Warum gibt das folgende Beispiel “0” aus und was muss sich ändern, damit es wie erwartet “1” ausgibt?

#include <iostream>
struct base {
   virtual const int value() const {
      return 0;
   }
   base() {
      std::cout << value() << std::endl;
   }
   virtual ~base() {}
};

struct derived : public base {
   virtual const int value() const {
      return 1;
   }
};

int main(void) {
   derived example;
}

  • Siehe auch diese Frage

    – richq

    30. Januar 2009 um 17:42 Uhr

Benutzer-Avatar
Sean Hell

weil base wird zuerst konstruiert und ist nicht zu einem “gereift”. derived noch. Es kann keine Methoden für ein Objekt aufrufen, wenn es nicht garantieren kann, dass das Objekt bereits ordnungsgemäß initialisiert ist.

  • Eine andere Sichtweise ist, dass im Konstruktor von „base“ der virtuelle Zeiger auf die virtuelle Tabelle der Klasse „blase“ zeigt und nicht auf „derived“, bis die Konstruktion von „base“ abgeschlossen ist es tritt in den Konstruktor von ‘derived’ ein

    – Jorge González Lorenzo

    18. Juni 2014 um 8:53 Uhr

  • Gibt es eine Möglichkeit dieses Verhalten zu umgehen. Ich weiß, dass es im Allgemeinen schlechtes Design ist, aber manchmal kann es für Rapid Prototyping nützlich sein.

    – Willem Van Onsem

    16. November 2014 um 8:14 Uhr

  • Ja, es gibt einige Möglichkeiten. Sehen C++ Lite.

    – Julian

    4. April 2016 um 3:09 Uhr

Wenn ein abgeleitetes Objekt konstruiert wird, muss der Basisklassenkonstruktor abgeschlossen werden, bevor der Hauptteil des abgeleiteten Klassenkonstruktors aufgerufen wird. Bevor der Konstruktor der abgeleiteten Klasse aufgerufen wird, ist der dynamische Typ des Objekts im Aufbau eine Basisklasseninstanz und keine abgeleitete Klasseninstanz. Aus diesem Grund können beim Aufrufen einer virtuellen Funktion von einem Konstruktor nur die Überschreibungen der virtuellen Funktion der Basisklasse aufgerufen werden.

  • Nachdem die Basisklasse ctor abgeschlossen ist, this und der virtuelle Funktionstabellenzeiger wird auf die abgeleitete Klasse gesetzt, und virtuelle Funktionsaufrufe werden dynamisch die abgeleitete Überschreibung via auswählen this->fn();. Der letzte Satz ist also nur wahr, wenn der Aufruf aus der Basisklasse ctor erfolgt.

    – Don Slowik

    29. September 2021 um 12:56 Uhr


Tatsächlich gibt es eine Möglichkeit, dieses Verhalten zu erreichen. “Jedes Problem in der Software kann mit einem Umweg gelöst werden.”

/* Disclaimer: I haven't done C++ in many months now, there might be a few syntax errors here and there. */
class parent
{
public:
     parent( ) { /* nothing interesting here. */ };
protected:
     struct parent_virtual
     {
         virtual void do_something( ) { cout << "in parent."; }
     };

     parent( const parent_virtual& obj )
     {
          obj.do_something( );
     }
};

class child : public parent
{
protected:
     struct child_virtual : public parent_virtual
     {
         void do_something( ) { cout << "in child."; }
     };
public:
      child( ) : parent( child_virtual( ) ) { }
};

  • Diese Methode ist sehr eingeschränkt, da sie nicht auf die Klassenmitglieder zugreifen kann, sondern nur auf die Strukturmitglieder. Die Texte sollten daher sein "in parent_virtual" und "in child_virtual" bzw.

    – Hallo Auf Wiedersehen

    27. September 2012 um 12:37 Uhr

  • Das lässt sich leicht beheben. Sie können sie nach Bedarf mit verschiedenen Klassen/Strukturen befreunden und diesen Zeiger übergeben.

    – Tanveer Badar

    17. September 2013 um 5:10 Uhr

  • Wie verwenden Sie diese Klassen? Wie würde Ihre Hauptfunktion aussehen, die der in der Frage entspricht? Sie haben kein Beispiel oder keine Anleitung angegeben.

    – Hallo Auf Wiedersehen

    17. September 2013 um 19:36 Uhr

  • dieser stil druckt “in parent”. Ich glaube, wir wollen “im Kind” ausdrucken

    – VarsMolta

    16. März 2015 um 14:24 Uhr

Die Frage, wie es funktioniert ist ein FAQ-Element.

Zusammenfassend, während der Klasse T konstruiert wird, ist der dynamische Typ Twodurch virtuelle Aufrufe von abgeleiteten Klassenfunktionsimplementierungen verhindert werden, die, falls zulässig, Code ausführen könnten, bevor die relevante Klasseninvariante eingerichtet wurde (ein häufiges Problem in Java und C#, aber C++ ist in dieser Hinsicht sicher).

Die Frage, wie die spezifische Initialisierung abgeleiteter Klassen in einem Basisklassenkonstruktor durchgeführt wird, ist ebenfalls ein FAQ-Artikeldirekt nach dem zuvor erwähnten.

Zusammenfassend kann die Verwendung von statischem oder dynamischem Polymorphismus die relevanten Funktionsimplementierungen bis zum Basisklassenkonstruktor (oder -klasse) weitergeben.

Eine bestimmte Möglichkeit, dies zu tun, besteht darin, a zu übergeben Objekt „Teilefabrik“. up, wo dieses Argument voreingestellt werden kann. Zum Beispiel ein General Button -Klasse könnte eine API-Funktion zur Erstellung von Schaltflächen an ihre übergeben Widget Basisklassenkonstruktor, sodass dieser Konstruktor das richtige Objekt auf API-Ebene erstellen kann.

Benutzer-Avatar
Wein

Du solltest nicht polymorph Rufen Sie die virtuellen Methoden vom Konstruktor auf. Stattdessen können Sie sie nach der Konstruktion des Objekts aufrufen.

Ihr Code kann wie folgt umgeschrieben werden

struct base {
   virtual const int value() const {
      return 0;
   }
   base() {
      /* std::cout << value() << std::endl; */
   }
   virtual ~base() {}
};

struct derived : public base {
   virtual const int value() const {
      return 1;
   }
};

int main(void) {
   derived example;
   std::cout << example.value() << std::endl;
}

  • Das ist absolut schlecht, da Sie diesen Code jedes Mal schreiben müssten, wenn Sie eines dieser Objekte erstellt haben, im Gegensatz zu nur einem Mal, wenn Sie es verwenden Tanveer Badar‘s Methode.

    – Hallo Auf Wiedersehen

    27. September 2012 um 12:33 Uhr

  • −1 Really Bad Advice™ für die Fälle, die von Interesse sind (obwohl es ein guter Rat für den uninteressanten Fall eines Aufrufs ist, der natürlich nach der Initialisierung erfolgen würde und nur unnatürlich und ohne vernünftigen Grund zum Konstruktor verschoben wurde).

    – Prost und hth. – Alf

    12. Mai 2016 um 9:59 Uhr

  • Was ist, wenn Sie nicht möchten, dass sich die Benutzer der Klasse um einen zusätzlichen Schritt kümmern müssen?

    – Flarn2006

    13. Oktober 2020 um 19:40 Uhr

Benutzer-Avatar
Hank Gay

Die allgemeine Regel ist, dass Sie keine virtuelle Funktion von einem Konstruktor aufrufen.

  • Das ist absolut schlecht, da Sie diesen Code jedes Mal schreiben müssten, wenn Sie eines dieser Objekte erstellt haben, im Gegensatz zu nur einem Mal, wenn Sie es verwenden Tanveer Badar‘s Methode.

    – Hallo Auf Wiedersehen

    27. September 2012 um 12:33 Uhr

  • −1 Really Bad Advice™ für die Fälle, die von Interesse sind (obwohl es ein guter Rat für den uninteressanten Fall eines Aufrufs ist, der natürlich nach der Initialisierung erfolgen würde und nur unnatürlich und ohne vernünftigen Grund zum Konstruktor verschoben wurde).

    – Prost und hth. – Alf

    12. Mai 2016 um 9:59 Uhr

  • Was ist, wenn Sie nicht möchten, dass sich die Benutzer der Klasse um einen zusätzlichen Schritt kümmern müssen?

    – Flarn2006

    13. Oktober 2020 um 19:40 Uhr

In C++ können Sie keine virtual/override-Methode von einem Konstruktor aufrufen.

Nun, es gibt einen guten Grund, warum Sie dies tun können. Als „Best Practice in Software“ sollten Sie möglichst vermeiden, zusätzliche Methoden von Ihrem Konstruktor aufzurufen, auch nicht virtuelle.

Aber es gibt immer eine Ausnahme von der Regel, daher möchten Sie vielleicht eine “Pseudo-Konstruktormethode” verwenden, um sie zu emulieren:

#include <iostream>

class base {
   // <constructor>
   base() {
      // do nothing in purpouse
   }
   // </constructor>

   // <destructor>
   ~base() {
      // do nothing in purpouse
   }
   // </destructor>

   // <fake-constructor>
   public virtual void create() {
      // move code from static constructor to fake constructor
      std::cout << value() << std::endl;
   }
   // </fake-constructor>

   // <fake-destructor>
   public virtual void destroy() {
      // move code from static destructor to fake destructor
      // ...
   }
   // </fake-destructor>

   public virtual const int value() const {
      return 0;
   }

   public virtual void DoSomething() {
      // std:cout << "Hello World";
   }
};

class derived : public base {
   // <fake-constructor>
   public override void create() {
      // move code from static constructor to fake constructor
      std::cout << "Im pretending to be a virtual constructor," << std::endl;
      std::cout << "and can call virtual methods" << std::endl;
   }
   // </fake-constructor>


   // <fake-destructor>
   public override void destroy() {
      // move code from static destructor to fake destructor
      std::cout << "Im pretending to be a virtual destructor," << std::endl;
      std::cout << "and can call virtual methods" << std::endl;
   }
   // </fake-destructor>

   public virtual const int value() const {
      return 1;
   }
};

int main(void) {
   // call fake virtual constructor in same line, after real constructor
   derived* example = new example(); example->create();

   // do several stuff with your objects
   example->doSomething();

   // call fake virtual destructor in same line, before real destructor
   example->destroy(); delete example();
}

Als Pluspunkt empfehle ich Programmierern, “struct” nur für Feldstrukturen und “class” für Strukturen mit Feldern, Methoden, Konstruktoren, … zu verwenden.

  • @umlcat -3 Downvotes, keine Erklärung !!!

    – umlkat

    14. September 2015 um 15:22 Uhr

  • Ich habe nicht abgelehnt, aber wie geschrieben, das ist falsch. Du kann Rufen Sie eine virtuelle Methode von einem ctor auf. Es ist vollkommen gültiges C++. Das eigentliche Problem ist, dass es nicht unbedingt das tut, was die meisten Leute erwarten, da es die Methode für die Klasse aufruft, deren ctor gerade ausgeführt wird, und nicht welchen dynamischen Typ dieses Objekt haben wird später, danach es ist konstruiert. Außerdem scheint “Methoden nicht in ctor aufrufen” ein schlechter Rat zu sein, und class vs struct macht keinen wirklichen unterschied.

    – Unterstrich_d

    21. Mai 2017 um 13:03 Uhr


1013520cookie-checkVirtuelle C++-Funktion vom Konstruktor [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy