Kopierkonstruktor und Clone Methode



  • Ich hab gerade eine Übung aus meinem Buch gemacht, und es geht gerade um Clone Methoden. Das heißt doch, wenn man jetzt ein Mammal Objekt hat das aus nem Mammal Objekt und nem Dog Objekt besteht:

    Mammal *Objekt=new Dog;
    

    dann kann man nicht so einfach eine tiefe Kopie vornehmen, da nur der Kopierkonstruktor von Mammal aufgerufen werden würde, und es nicht weiter bis zum Kopierkonsturktor von Dog gehen würde da man Kopierkonstruktor, und Konstruktor allgemein nicht als virtuell deklarieren kann? Stimmt doch so weit oder?
    Deshalb macht man sich eine Clone Methode und die kopiert den Dog Teil in das Dog Mammal Objekt:

    Mammal * Clone() const {return new Dog (*this);}
    

    und die Clone MEthode kann man ja virtuell machen, und somit gehts auch weiter bis zum Dog Objekt...
    Und wenn man das ganze nicht machen würde dann würden beide Objekte am Schluss die gleiche Adress haben, und das ist ja BÖÖÖÖSE!

    Mammal *ptr=new Dog(12,60);
    Mammal *test;
    test=ptr;
    

    Also so irgendwie stell ich mir das vor, hier mal der Code zu dem was ich jetzt die ganze Zeit gelabbert hab:

    #include <iostream>
    using namespace std;
    
    class Mammal
    {
        public:
        Mammal(int pAge);
        Mammal(const Mammal &rhs);
        virtual ~Mammal();
        int GetAge() const {return *Age;}
        void SetAge(int change) {*Age=change;}
        virtual void Speak() const {cout << "Tier spricht\n";}
        virtual Mammal * Clone() const {return new Mammal(*this);}
    
        private:
        int *Age;
    };
    
    Mammal::Mammal(int pAge)
    {
        Age=new int;
        *Age=pAge;
    }
    
    Mammal::Mammal(const Mammal &rhs)
    {
        cout << "Kopierkonstruktor MAMMAL\n";
        Age=new int;
        *Age=rhs.GetAge();
    }
    
    Mammal::~Mammal()
    {
        delete Age;
    }
    
    class Dog : public Mammal
    {
        public:
        Dog(int pAge, int base_class_pAge);
        Dog(const Dog &rhs);
        ~Dog();
        int GetAge() const {return *Age;}
        void SetAge(int change) {*Age=change;}
        void Speak() const {cout << "Hund bellt\n";}
        Mammal * Clone() const {return new Dog (*this);}
    
        private:
        int *Age;
    };
    
    Dog::Dog(int pAge, int base_class_pAge)
    :Mammal(base_class_pAge)
    {
        Age=new int;
        *Age=pAge;
    }
    
    Dog::Dog(const Dog &rhs)
    :Mammal(rhs)
    {
        cout << "Kopierkonstruktor DOG\n";
        Age=new int;
        *Age=rhs.GetAge();
    }
    
    Dog::~Dog()
    {
        delete Age;
    }
    
    int main()
    {
    
        Mammal *ptr=new Dog(12,60);
        Mammal *test;
        test=(ptr->Clone());
        cout << ptr->GetAge() << endl;
        cout << test->GetAge() << endl;
        ptr->SetAge(333);
        cout << ptr->GetAge() << endl;
        cout << test->GetAge() << endl;
    
        return 0;
    }
    

    Nun stellt sich mir nur noch die Frage, warum wird eigentlich der Kopierkonstruktor aufgerufen. Also von Mammal und von Dog....ich hab die die Clone Methode, die macht doch alles für mich oder? Was will der Kopierkonstruktor von mir?
    Also irgendwie hab ichs nur so halb gechekt, ich weiß jetzt zwar wie ichs anwenden soll, aber irgendwie noch nicht ganz genau wies funktioniert, und das ist ja des wichtigste.
    Also wenn jemadn weiß was ich meine und des checkt, dann wärs nett wenn ers mir nochmal verdeutliche könnte.



  • Mammal * Clone() const {return new Dog (*this);}
    

    hier wird der kopierkonstruktor ( new Dog(*this); ) aufgerufen.

    im übrigens sollte diese funktion auch ein Dog* zurückgeben:

    Dog* Clone() const {return new Dog (*this);}
    


  • Mh, also ich hab halt auch den Rückgabetyp Mammal genommen weil des so in meinem Buch stand. Und ist es den nicht so das man bei virtuellen Funktionen den gleichen Rückgabetyp benötigt? Außerdem ist es doch egal, da ein Dog Objekt doch auch gleich ein Mammal Objekt ist?
    Bzw. wo ist den der Unterschied wenn ich Mammal anstatt Dog zurückgebe?
    Dankeschön schon mal im Voraus.



  • queer_boy schrieb:

    ...
    im übrigens sollte diese funktion auch ein Dog* zurückgeben:

    Dog* Clone() const {return new Dog (*this);}
    

    Dann bekommt man aber bestimmt eine Compilerwarnung um die Ohren gekloppt, weil bei einer überschriebenen Funktion der Returntyp geändert wird - das mag der Compiler nicht.

    Gruß,

    Simon2.



  • Also äh funktionieren tut bei mir beides. Wahrscheinlich hab ich auch nen "schlechten" Compiler (MinGW) weil da kommt bei mir keine Fehlermeldung.
    Kann man jetzt sagen: "Es ist egal ob man 'Mammal *' oder 'Dog *' zurückgibt!"?
    Dankeschön schon mal im Voraus.



  • Hi,

    also ich glaube nicht, dass Dein Compiler "schlecht" ist, aber vielleicht hast Du nicht alle Warnings angeschaltet (-Wall oder -pedantic) ... oder verschiedene Compiler handhaben das eben unterschiedlich.

    Ich weiß aus dem Stand auch nicht, was der Standard dazu sagt, meine aber, mich an solche Warnings (beim gcc) zu erinnern.

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Dann bekommt man aber bestimmt eine Compilerwarnung um die Ohren gekloppt, weil bei einer überschriebenen Funktion der Returntyp geändert wird - das mag der Compiler nicht.

    Clone ist weiter oben virtuell 😉

    @Stromberg:
    Wenn du in deiner Basisklasse eine virtuelle Methode hast, die einen Zeiger oder eine Refernez auf den eigenen Typ zurückgeben, dann kannst du in abgeleiten Klassen dieser virtuellen Funktion als Rückgabetyp einen Zeiger/Referenz auf die abgeleitete Klasse geben:

    class A {
    
       public:
               virtual A& func();
    };
    
    class A : public B {
    
       public:
               virtual B& func();
               // Alles im grünen Bereich ;), das virtuell kann man aber auch hier
               // weglassen, würde ich aber trotzdem hinzuschreiben, damit man
               // sieht, dass die Funktion virtuell ist.
    };
    

    Das ganze nennt sich irgendwas mit Covariant-Returntyp ( *geedit* 😉 )



  • KasF schrieb:

    ...
    Clone ist weiter oben virtuell ;)...

    Ich weiß ... aber nicht, wieso Du das sagst. 😕
    Wie würdest Du denn diesen Zusammenhang beschreiben wenn nicht mit "overwrite"/"überschreiben" ?

    KasF schrieb:

    ...Das ganze nennt sich irgendwas mit Covariant-Returntyp ( *geedit* 😉 )

    Das kann natürlich sein - das weißt Du dann besser als ich.

    Gruß,

    Simon2.



  • Simon2 schrieb:

    aber nicht, wieso Du das sagst.

    Weil du ja geschrieben hattest, dass der Compiler hier warnen würde, was aber offiziel erlaubt ist, wenn die Funktion virtuell ist.

    Oder habe ich dich falsch verstanden ?



  • ja, KasF hat recht- covariant return types. und der vorteil liegt darin, dass du nicht explizit runtercasten musst, wenn du clone für ein objekt der abgeleiteten klasse aufrust.

    Dog d;
    
    Dog *x = d.clone(); //Fehler ohne covariant return type
    


  • KasF schrieb:

    Simon2 schrieb:

    aber nicht, wieso Du das sagst.

    Weil du ja geschrieben hattest, dass der Compiler hier warnen würde, was aber offiziel erlaubt ist, wenn die Funktion virtuell ist.

    Oder habe ich dich falsch verstanden ?

    Jain - Mein Mißerständnis lag IMO nicht darin, dass die Funktion virtuell sei oder nicht, sondern darin, dass ich die "Toleranz gegenüber covariant return types" nicht kannte, sondern nur von der harten Haltung von Compilern gegenüber variieren return types wusste. 😉

    Also wie auch immer: queer boys Hinweis war richtig und meiner falsch. 😃

    Gruß,

    Simon2.



  • KasF schrieb:

    class A {
       
       public:
               virtual A& func();
    };
    
    class A : public B {
       
       public:
               virtual B& func();
               // Alles im grünen Bereich ;), das virtuell kann man aber auch hier
               // weglassen, würde ich aber trotzdem hinzuschreiben, damit man
               // sieht, dass die Funktion virtuell ist.
    };
    

    es ist nicht compilierbar unter VC6 sowie

    virtual Dog* Clone() const {return new Dog (*this);}
    

    error C2555: 'B::func' : overriding virtual function differs from 'A::func' only by return type or calling convention.
    hab aber kein problem unter vc7



  • @Simon2 wenn ich jetzt gerade nicht völlig daneben liege, wird sich kein compiler darüber beschwere, da
    Dog* Clone()
    und
    Mammal* Clone()
    schlicht zwei verschiedene funktionen sind. wenn man im kind Mammal* Clone() implementiert, muss man halt nur darauf achten, dass man nie
    Dog* hund=Mammal::Clone() ausführt, da dann die funktion des elternobjekte aufgerufen wird, also Dog* Dog::Clone().



  • Was soll die Methode Clone eigentlich für einen Sinn haben? Die Bezeichner tiefe und flache Kopie finde ich auch schwachsinnig, entweder man kopiert ein Objekt oder eben nicht.
    Was ist der Vorteil hiervon:

    Mammal* dog = new Dog();
    Mammal* dog2 = dog->Clone();
    

    gegenüber hiervon?

    Mammal* dog = new Dog();
    Mammal* dog2 = new Dog( *dog );
    

    Das "Clone" kenne ich so nur von Java, dort hat's sicherlich auch seine Berechtigung, in C++ ist es imho aber Unsinn. Es ist völlig normal und etabliert, Kopien mit dem Kopierkonstruktor zu erzeugen, wozu also Clone? Keep things simple as possible, oder so ⚠



  • ghorst schrieb:

    @Simon2 ...

    Du weißt aber schon, dass das Thema in diesem Thread schon vor fast einem halben Jahr abgeschlossen wurde, oder ? Die Antwort steht auch schon weiter oben. 😉 😃

    Gruß,

    Simon2.



  • @simon2 ich habe den nicht ausgegraben und zu meiner schande muss ich gestehen, schaute ich nicht, wann du deinen beitrag hier geschrieben hast. sollte ich aber in zukunft wieder machen. 😉



  • ghorst schrieb:

    @simon2 ich habe den nicht ausgegraben ...

    Weiß ich wohl - ist mir auch schon öfter passiert. Kein Problem !
    😃

    Gruß,

    Simon2.


Anmelden zum Antworten