copy constructor von parent und child aufrufen
-
hallo zusammen,
ich habe ein objekt array der klasse parent, von der klasse childA und childB erben. wenn ich das objArray mit dem copy ctor kopiere, dann wird nur der copy ctor von parent aufgerufen.
wie schaffe ich es, dass der copy ctor von parent und auch der copy ctor von childA bzw. childB aufgerufen werden?danke!
-
ich könnte natürlich beim kopieren den typ casten und den child copy ctor aufrufen, der dann wieder den parent copy ctor aufruft. aber ich hoffe, das geht auch implizit irgendwie?
-
1. Mehrfachvererbung - bitte überleg dir das nochmal. Überhaupt die Vererbung - öffentliche Vererbung bedeutet "ist ein" und Parent ist bestimmt kein Child (Okay, streng genommen sind Eltern auch Kinder, weil sie auch Eltern haben, aber nur im echten Leben).
2. Du hast wahrscheinlich einen eigenen Copy-Construktor definiert und dabei die Konstruktoren der Basisklassen nicht in der Initialisierungsliste aufgerufen:Parent::Parent(Parent const &in) : childA(in), childB(in) //das hier { }
-
@wxSkip: Es scheint genau anders herum zu sein. childA und childB erben von parent. Sein Problem ist, dass er ein Array von parents hat. Wenn da ein childA reinbebastelt wird, kommt es zu slicing; danach ist ein childA nur noch ein parent.
Die einzige Lösung: Deine Klassen polymorph gestalten und ein Array von Pointern verwenden, gleichzeitig Kopien vermeiden und nur mit Pointern/Referenzen arbeiten, damit die Typinfo nicht verloren geht.
-
Sabrina S. schrieb:
@wxSkip: Es scheint genau anders herum zu sein. childA und childB erben von parent. Sein Problem ist, dass er ein Array von parents hat. Wenn da ein childA reinbebastelt wird, kommt es zu slicing; danach ist ein childA nur noch ein parent.
Die einzige Lösung: Deine Klassen polymorph gestalten und ein Array von Pointern verwenden, gleichzeitig Kopien vermeiden und nur mit Pointern/Referenzen arbeiten, damit die Typinfo nicht verloren geht.Hab ich das tatsächlich so falsch gelesen oder wurde das nacheditiert
Du hast natürlich recht. Wenn man jetzt allerdings das Array kopiert, zeigen beide Arrays auf die selben Objekte. Wenn die Objekte kopiert werden sollen, kommen eigentlich nur Factory Method ("virtueller Konstruktorersatz" + böses Zeug wie z.B. virtueller Zuweisungsoperator) oder Prototype (also die Clone()-Methode) in Frage, da Kopierkonstruktoren ja nicht virtual sein können. Wie gesagt, ist das natürlich nur nötig, wenn man die Objekte kopieren will (was nicht so oft der Fall sein sollte).
-
hallo,
danke für eure antworten.
ich wollte die objekte schon gerne kopieren, um einen backup des objektarrays zu erstellen.
nach einigem rumprobieren unter berücksichtigung eurer anregungen klappt es nun wie in meinem zweiten post beschrieben. durch casten beim kopieren kommen die passenden copy konstruktoren zum einsatz. leider ist es recht unelegant, aber was solls.
-
mael15 schrieb:
hallo,
danke für eure antworten.
ich wollte die objekte schon gerne kopieren, um einen backup des objektarrays zu erstellen.
nach einigem rumprobieren unter berücksichtigung eurer anregungen klappt es nur wie in meinem zweiten post beschrieben. durch casten beim kopieren kommen die passenden copy konstruktoren zum einsatz. leider ist es recht unelegant, aber was solls.Das ist SEHR böse. Hast du den Vorschlag bzgl. Prototype (die Clone()-Methode) gelesen?
EDIT: Backup -> Memento-Pattern?
-
Gib deinem parent eine rein virtuelle clone methode: virtual Parent* clone()=0;
In den Childklassen muss du diese dann jeweils implementieren, mit korrektem Aufruf des CopyConstructors.Alles was du dann in Parent im CopyConstructor brauchst, ist pro Element einmal clone aufrufen, und die Rückgabe entsprechend abspeichern.
-
danke für den vorschlag. das problem ist leider, dass man keine objekte abstrakter klassen in ein wxObjArray speichern kann. ich kann dann also kein array von parents mehr verwenden.
das memento pattern sieht interessant aus! ich befürchte nur, das wird zu aufwändig bei meinen objekten.
was ist denn so böse an dem casten, abgesehen davon, dass es nicht sauber und elegant ist?
-
mael15 schrieb:
das problem ist leider, dass man keine objekte abstrakter klassen in ein wxObjArray speichern kann. ich kann dann also kein array von parents mehr verwenden.
Oben bereits erwähnt, dass das wg. Slicing sowieso Müll ist... (Hast du schon mal danach gesucht?)
Mit Pointern im Array (bitte schau gleich, dass du mit std::vector oder gar boost::ptr_vector o.Ä. arbeitest) sind Basisklassenzeiger (auch auf abstrakte Klassen) kein Problem mehr.
-
mael15 schrieb:
was ist denn so böse an dem casten, abgesehen davon, dass es nicht sauber und elegant ist?
nicht sauber und nicht elegant sollten bereits Grund genug sein. Abgesehen davon musst du selber Typinformationen mitschleppen (ein parent ist nunmal nur ein parent und kein childA oder childB), und childA ist kein childB. Um den cast richtig zu machen, brauchst du den genauen Typ.
Und jetzt such nach bitte Slicing, denn dann willst du NIE NIE MEHR Objekte abgeleiteter Klassen in Arrays von Basisklassenobjekten stecken!
-
hi sabrina,
ich habe mir slicing angeschaut und kann das problem natürlich grundsätzlich nachvollziehen. aber ich kann das problem durch dynamic_cast vollständig umgehen. insofern frage ich mich, ob es der mühe wert wäre.
dazu kommt, dass ich gerne mit den wx klassen arbeite und nur ungerne std::vector nehmen würde. leider ist wxVector im momentaten stable release 2.8 noch nicht enthalten, kommt aber bald.
-
ich habe mir deinen vorschlag jetzt in meiner todo liste zum stable release von wxWidgets 3.0 genommen. danke!
-
mael15 schrieb:
ich habe mir slicing angeschaut und kann das problem natürlich grundsätzlich nachvollziehen. aber ich kann das problem durch dynamic_cast vollständig umgehen.
Und wie? Sind Member der Subklasse mal abgeschnitten, bekommst du die nimmer zurück.
-
wenn ich einen parent aus dem array geholt habe, dann klappt ein dynamic_cast bei erhaltung aller werte problemlos:
class parent{ public: parent(int z){ zahlParent = z; }; int zahlParent; virtual void dummy(){}; }; class childA : public parent { public: childA(int z) : parent(z){ zahlChild = z * 2; }; int zahlChild; }; parent *par = new childA(5); childA *ca = dynamic_cast<childA*>(par); // childA.zahlChild == 10
-
Und wo ist in deinem Beispiel das Array? Das was du machst geht wunderbar, ruft aber kein Slicing hervor.
parent p = childA(5); std::cout << p.zahlParent << " " << static_cast<childA&>(p).zahlChild << std::endl;
- dynamic_cast geht in dem Fall nicht. Wirft zur Laufzeit einen std::bad_cast (bei Pointern würdest du nen nullptr zurück bekommen). Deshalb brauchen wir hier einen static_cast (Wie gesagt, musst du sowieso selber Typinformationen mitschleppen, in nem parent-array liegen nur parents, um jetzt zu wissen, ob da ein parent, childA oder childB gespeichert wurde um korrekt casten zu können, musst du das abfragen können).
- In p ist nur der Basisklassenanteil von childA enthalten - wie auch, ist ja nicht mehr Speicher vorhanden!
- Aus 2 resultiert, dass nach dem static_cast zahlChild einen beliebigen Wert enthält.
In einem
parent parentArray[10];
z.B.liegen auch nur parents als Value, es ist nicht mehr Speicher vorhanden als 10 parents brauchen, deshalb fehlt nach einem cast nach childA definitiv die vorherige Belegung von zahlChild!DAS IST SLICING!
-
hmmmm, vielleicht ist es ein unterschied, ob man ein wxObjArray oder ein standardarray a la parents[] benutzt. im meinem ersten post hatte ich objArray geschrieben.
mein oben angegebenes beispiel funktioniert genauso, wenn ich ein wxObjArray benutze, deswegen hatte ich es weggelassen:ArrayOfParents *parentsAr = new ArrayOfParents(); parentsAr->Add(new childA(5)); childA *ca = dynamic_cast<childA*>(&parentsAr->Item(0)); // childA.zahlChild == 10
im header zusätzlich zur klassendef:
WX_DECLARE_OBJARRAY(parent, ArrayOfParents);
in der cpp datei oben:
#include <wx/arrimpl.cpp> WX_DEFINE_OBJARRAY(ArrayOfParents);