Flache Kopie von Klassen mit polymorph überladenen Membern vermeiden
-
Ich benötige eine Kopie von einem Objekt, dessen Klasse einen Zeiger auf eine virtuelle, polymorph überladene Klasse besitzt. Eine flache Kopie will ich dabei vermeiden, da das Quell-Objekt vor dem Ziel-Objekt gelöscht werden könnte.
Hier mal ein Beispiel (BCB, ich gehe aber von einer relativen IDE-Unabhängigkeit aus):
class Eigenschaft { public: virtual String AlsString() = 0; virtual void VonString(String s) = 0; virtual ~Eigenschaft(){}; }; class IntEigenschaft: public Eigenschaft { public: String AlsString(){return IntToStr(Wert);}; void VonString(String s){Wert = StrToInt(s);} private: int Wert; }; class FloatEigenschaft: public Eigenschaft { public: String AlsString(){return FloatToStr(Wert);}; void VonString(String s){Wert = StrToFloat(s);} private: float Wert; }; class Klasse { public: Klasse(){}; Klasse(const Klasse& k) { Name = k.Name; Eig = k.Eig;//Flache Kopie! }; ~Klasse() { delete Eig; } String Name; Eigenschaft *Eig; };Die Klassen IntEigenschaft und FloatEigenschaft implementieren die virtuelle Klasse Eigenschaft. Diese wird in der Klasse Klasse verwendet.
Führe ich den folgenden Code aus, bekomme ich wie erwartet eine Zugriffsverletzung.
Klasse *asdf = new Klasse(); asdf->Name = "Klasse 1"; asdf->Eig = new IntEigenschaft(); asdf->Eig->VonString("1"); Klasse *jkl = new Klasse(*asdf); delete asdf; ShowMessage(jkl->Eig->AlsString());//Zugriffsverletzung (ShowMessage ist BCB-Funktion) delete jkl;Die flache-Kopie-Vermeidung sieht ja vor, ein neues Objekt der Variable zu erzeugen. Das kann ich aber aufgrund der virtuellen Basisklasse nicht tun.
Ich hatte schon eine Eigenschaft Klone()*-Funktion in der Eigeschaft- und den abgeleiteten Klassen im Sinn, die mit einem neuen Zeiger ihren eigenen Konstruktor aufruft, den Wert des aktuellen Objektes kopiert und den Zeiger zurückgibt:
class Eigenschaft { ... virtual Eigenschaft* Klone() = 0; ... } class IntEigenschaft { ... Eigenschaft *Klone() { Eigenschaft* tmp = new IntEigenschaft(); tmp->Wert = Wert; return tmp; } ... }Diese Funktion habe ich aber im Verdacht, unschöne Dinge im Speicher anzustellen. Ich kann es nicht genauer beschreiben, ich spiele seit Wochen ein kleines Ratespiel im Zusammenhang mit absurden Zugriffsverletzungen. Hat jemand eine bessere Idee? Oder sieht jemand einen fehlerhaften Grundgedanken?
-
Das mit der Klone-methode ist schon der richtige Ansatz. Du solltest aber den Eig-Pointer im Konstruktor mit 0 initialisieren damit dein Destruktor nicht daneben gehen kann.
-
Im "echten" Projekt habe ich das NULL-setzen drin, nur hier beim Testprojekt hab ich nicht dran gedacht.
Die Klone-Methode: Ich bin wohl der Einzige, der ein wenig bedrückt ist, wenn er etwas richtig gemacht hat... denn das bedeutet, dass meine wandernde Zugriffsverletzung eine andere Ursache hat. Danke für das Mit-Denken.
-
Nur als Hinweis, Du kannst zum Klonen auch den (impliziten) Copy-Constructor der abgeleiteten Klassen benutzen:
IntEigenschaft* Klone() { return new IntEigenschaft( *this ); }
-
oder IntEigenschaft einen Konstruktor mit einem int-Argument geben.
-
Oh, hab vergessen, zu antworten. Wie unhöflich von mir.
Stimmt, ein Aufruf vom Konstruktor (egal ob Copykonstruktor oder überladener Konstruktor) ist natürlich ordentlicher. Besonders recht, wenn die Klasse mal zusätzliche Eigenschaften bekommen sollte. Danke für den Hinweis.