Konstruktorproblem bei Mehrfachvererbung



  • Hallo zusammen,

    ich möchte gerne den berühmten "Dimond of Death" nachprogrammieren, rein zu Übungszwecken.

    In einem ersten Programm, das etwas komplexer ist, hatte ich das Problem, dass sobald die 1. abgeleitete Klasse die Mutter-Aller-Klassen aufgerufen hat, der Default-Konstruktor verwendet wurde.

    Daher habe ich mir ein einfacherers Bsp. auis dem Netz geladen. Dieses sieht wie folgt aus:

    #include<iostream>
        using namespace std;
    
        class UltimateBase
        {
        private:
          int value;
        public:
          UltimateBase(int value)
          : value(value)
          {}
    
          int print() const
          {
            return value;
          }
        };
    
        class Base1 : virtual public UltimateBase
        {
        public:
          Base1(int value) : UltimateBase(value)
          {}
        };
    
        class Base2 : virtual public UltimateBase
        {
        public:
          Base2(int value) : UltimateBase(value)
          {}
        };
    
        class Derived : public Base1, public Base2
        {
        public:
          Derived(int a, int b)
          : Base1(a), Base2(b)
          {}
        };
    
        int main()
        {
          Derived d(3,4);
          cout<<d.print()<<'\n';
        }
    

    Quelle:http://tutorial.schornboeck.net/mehrfach_vererbung.htm

    Wenn ich nun dieses Bsp. kompiliere erhalte ich die Fehlermeldung error C2512: 'UltimateBase::UltimateBase': Kein geeigneter Standardkonstruktor verfügbar. Warum checkt der Compiler nicht das doch alle Parameter für den deklarierten Konstruktor vorhanden sind? Ich benutze den Visual Studio 2008 C++ Kompiler (Express Edition).

    Für eine Antwort bin ich sehr Dankbar.



  • Der Konstruktor von Derived muss den Konstruktor von UltimateBase aufrufen. Die UltimateBase-Aufrufe in den Konstruktoren von Base1 und Base2 werden ignoriert. Die könnten ja unterschiedlich sein.



  • Gut,
    wenn ich einen default Konstruktor implementiere, dann kann ich dieses Verhalten auch erkennen, nur verstehen tue ich das nicht. Base1 und Base2 sind ja von UltimateBase abgeleitet. Das kann ja UltimateBase nicht "wissen". Daher verstehe ich nicht wie es von diesem Punkt aus weitergehen soll. Ich kann ja wohl schlecht für jede Klasse die ich irgendwann mal vererben möchte einen default-Konstruktor erstellen, und nach dem das Obkjekt erzeugt wurde in den abgeleiteten Klassen die Basisklasse mit Werten füllen.



  • JFisch schrieb:

    Gut,
    wenn ich einen default Konstruktor implementiere, dann kann ich dieses Verhalten auch erkennen, nur verstehen tue ich das nicht. Base1 und Base2 sind ja von UltimateBase abgeleitet. Das kann ja UltimateBase nicht "wissen". Daher verstehe ich nicht wie es von diesem Punkt aus weitergehen soll. Ich kann ja wohl schlecht für jede Klasse die ich irgendwann mal vererben möchte einen default-Konstruktor erstellen, und nach dem das Obkjekt erzeugt wurde in den abgeleiteten Klassen die Basisklasse mit Werten füllen.

    UltimateBase kann das nicht wissen. Aber Derived muss wissen, dass unten UltimateBase sitzt. Über welchen Pfad soll denn value in UltimateBase initialisiert werden? Über Base1 oder Base2?
    Daher:

    class Derived : public Base1, public Base2
    {
    public:
        Derived(int a, int b)
            : Base1(a), Base2(b), UltimateBase(a) //oder UltimateBase(b)
        {}
    };
    


  • Hallo Tachyon,

    das funktioniert. Nun bin ich aber etwas enttäuscht.
    Warum? Ganz einfach, nun legt man ja genau fest wie das Basisobjekt zu erstellen ist. D.h. der Programmiere muss wissen, dass wenn er eine Klasse erzeugt die Base1 und Base2 als Vater hat, diese wiederum beide UltimateBase als Vater haben. Daher hatte ich gehofft es gibt eine noch elegantere variante.
    Aber nun gut, vielen Dank euch beiden!



  • JFisch schrieb:

    Nun bin ich aber etwas enttäuscht.
    Warum? Ganz einfach, nun legt man ja genau fest wie das Basisobjekt zu erstellen ist. D.h. der Programmiere muss wissen, dass wenn er eine Klasse erzeugt die Base1 und Base2 als Vater hat, diese wiederum beide UltimateBase als Vater haben. Daher hatte ich gehofft es gibt eine noch elegantere variante.

    Wie soll das denn funktionieren? Welche Ausgabe erwartest du bei deinem Programm? 3 oder 4?



  • Wie soll das denn funktionieren? Welche Ausgabe erwartest du bei deinem Programm? 3 oder 4?

    Das Problem ist mir bewusst. Eine möglichkeit wäre frei nach dem Sprichwort, "Der frühe Vogel fängt den Wurm" dass der 1. Wert rausgegeben wird. Man könnte soetwas ja einfach in der Compilerdokumentation festhalten à la, "wenn dieses Problem auftritt, wird immer der 1. Wert, bzw. das 1. Objekt gewählt". Dann wäre es definiert und alle Programmierer wüssten es.
    Nun aber habe ich das Problem, dass wenn ich ein Derived Objekt erzeugen möchte, immer noch dran denken muss, dass ich die UltimateBase mit erzeuge. Bei einem so kleinen Programm evtl. noch möglich, aber bei einem größeren...
    Daher hatte ich gehofft das der Compiler das festlegt, nach einer Regel die dokumentiert ist.


Anmelden zum Antworten