virtuelle copy - construktoren



  • Hallo Leute

    Ist es üblich, dass man virtuelle Copy - Contructoren macht? Ich habe so in etwa folgendes Problem:

    class KB_Object{
    public:
     int i;
    
     KB_Object(void){
      this->i = 2;
     }
     virtual KB_Object(const KB_Object &rObj){
      this->i = 2;
     }
    };
    
    class KB_Inherit:public KB_Object{
    public:
     int k;
    
     KB_Inherit(void):KB_Object{
      this->k = 4;
     }
     KB_Inherit(const KB_Inherit &rInh):KB_Object(){
      this->k = 4;
     }
    };
    
    KB_Object *pArrObj[2];
    pArrObj[0] = new KB_Inherit();
    pArrObj[1] = new KB_Inherit(*pArrObj[0]);
    

    Wenn nun der copy - constuktor von KB_Object nicht virtuell ist, dann hat k einen undefinierten Wert!



  • ist das ein april scherz?



  • Ishildur schrieb:

    Hallo Leute

    Ist es üblich, dass man virtuelle Copy - Contructoren macht? Ich habe so in etwa folgendes Problem:

    Konstruktoren können niemals virtuell sein. Man spricht nur manchmal im Zusammenhang mit Fabrik-Methoden von "virtuellen Konstruktoren".

    mfg



  • Ishildur schrieb:

    Wenn nun der copy - constuktor von KB_Object nicht virtuell ist, dann hat k einen undefinierten Wert!

    Wenn diese Methode virtuell wäre, würde der Konstruktor von KB_Object gar nicht aufgerufen werden, weil der sehr schlaue Compiler dann mit Hilfe der V-Table die überladene Konstruktor-Methode aufrufen würde - dann hätte i einen undefinierten Wert.

    mfg

    PS: void als Parameter wird nur in C gebraucht.



  • @Terraner

    Ja, aber der überladene Konstruktor ruft ja den superkonstruktor auf! Bei virtuellen Destruktoren hätte ja man sonst dasselbe Problem...

    Aber sag mir, wie mein Problem zu lösen ist, wenn du es so genau zu wissen scheinst!
    Es geht darum, dass man den kopletten Inhalt einer Liste kopieren muss, welche aus unterschiedlichen, abgeleiteten Objekten besteht. Nicht nur die Pointer, sondern eben die ganzen Objekte müssen kopiert werden!!



  • Aber sag mir, wie mein Problem zu lösen ist, wenn du es so genau zu wissen scheinst!

    Der entscheidene Hinweis wurde von terraner bereits gegeben.
    Implementiert wird das dann so:

    // wir wollen "non-leaf"-Klassen doch lieber abstrakt machen
    class KB_Object{
    public:
        // "virtueller Copy-Konstruktor"
       virtual KB_Object* clone() const = 0; 
       ...
    };
    
    class KB_Inherit1 : public KB_Object{
    public:
     KB_Inherit1(const KB_Inherit &rInh);
    
     KB_Object* clone() const {
          return new KB_Inherit1(*this);
     }
    
    };
    
    class KB_Inherit2 : public KB_Object{
    public:
     KB_Inherit2(const KB_Inherit &rInh);
    
     KB_Object* clone() const {
          return new KB_Inherit2(*this);
     }
    
    };
    

    Kopieren einer polymorphen Liste ist dann ganz einfach:
    for each o in List {
    newList.add(o->clone());
    }



  • wenn man etwas über die anweisung

    pArrObj[1] = new KB_Inherit(*pArrObj[0])
    

    nachdenkt, dann versteht man es auch. wie kann der copy-konstruktor von KB_Inherit aufgerufen werden, wenn das argument garnicht vom typ KB_Inherit ist?! solange die klasse statisch ist, erkennt der compiler nur, daß *pArrayObj[0] vom typ KB_Object ist. und man kann ja kein KB_Inherit-objekt mittels copy-konstruktor aus einem KB_Object-objekt erzeugen!

    *pArrayObj[0] ist vom typ KB_Object. da die klasse KB_Inherit keinen copy-konstuktor mit einem argument vom typ KB_Object besitzt, ruft der compiler stattdessen den konstruktor von KB_Object auf (es gelten beim konstruktor-aufruf die selben regeln wie beim aufruf anderer überlandener funktionen). der hat nämlich ein argument vom typ KB_Object. der konstruktor von KB_Inherit wird dann nicht aufgerufen und k bleibt uninitialisiert.

    ist der copy-konstruktor von KB_Object jedoch virtuell, dann auch der aller abgeleiteten klassen. also auch der von KB_Inherit. beim aufruf von new KB_Inherit(*pArrayObj[0]) kann der compiler dann erkennen, daß, obwohl der statische typ von *pArrayObj[0] KB_Object ist, der eigentliche (dynamische) typ KB_Inherit ist. deshalb ruft er dann den copy-konstruktor von KB_Inherit auf und k wird initialisiert.

    virtuelle konstruktoren sind möglich und dies ist ein fall, wo sie auch sinvoll sind.

    ich hoffe, daß diese erklärung wenigstens halbwegs verständlich ist 😞

    EDIT:
    eigentlich sind virtuelle konstruktoren in diesem falle auch nicht sinnvoll. eigentlich sind sie nirgendwo sinnvoll. es wäre besser, der compiler würde einen typenfehler ausgeben. da er das aber nicht tut muß man das konstruktor-argument auf KB_Inherit casten.



  • Der Grund, warum Konstruktoren nicht virtuell sind und auch nicht sein können, liegt an einer fundamentalen Entscheidung zur C++ Sprache.

    Das Typsystem ist statisch. Alle Typen sind zur Compilezeit definiert und konstant. Da ein Konstruktor-Aufruf in C++ eine statische Funktion ist, ist es nicht möglich, auf andere Konstruktoren zu springen. Der gerufene Konstruktor (und damit das erzeugt Objekt von einem bestimmten Typ) steht fest.

    In einem C++-Programm kann ein Typ niemals ohne einen Wert existieren. Es ist nicht möglich, "reine" Typen herrumzureichen und an Funktionen zu übergeben (um dann z.B. davon den Konstruktor aufzurufen). Was wiederum daran liegt, dass das Typsystem statisch ist.

    Sprachen wie z.B. Delphi erlauben es, dass (die meisten) Typen wie Objekte herumgereicht werden können (in Delphi z.B. Meta-Klassen genannt). Dadurch ist es dann auch möglich, virtuelle Konstruktoren zu definieren.

    Da dies in C++ nicht möglich ist, werden Objektfabriken zur dynamischen Objekterzeugung eingesetzt. In einem Buch deiner Wahl werden die bestimmt gut beschrieben 🙂


Anmelden zum Antworten