Kopierkonstruktor in einer 3 Klassen Hierarchie mit is-a und has-a
-
Hallo, ich habe folgende 3 Klassen und will davon gültige, eigenständige Kopien erzeugen. Das hier ist das erste Mal, dass ich den Kopierkonstruktor praktisch anwenden will, deswegen würde ich eure Hilfe bei folgenden Fragen sehr schätzen:
1, Werden meine Objekte mit den diesen Kopierkonstruktoren ordentlich kopiert oder gesliced? (=nur Kopierkonstruktor von Basisklasse wird benutzt)
2, Ist das der richtige Weg an so ein Problem heranzugehen?
3, (eine nicht zum Titel passende Frage die im Code an der betreffenden Stelle dabeisteht.)btw, ich habe auch operator= überladen, lasse das aber erstmal raus weil ich mir nicht sicher bin ob ich das überhaupt brauche. Die Implementierung sollte sowieso genau gleich aussehen wie der Kopierkonstruktor.
**
SpriteObject**class SpriteObject { public: LPDIRECT3DTEXTURE9 texture; SpriteObject::SpriteObject(const SpriteObject& obj); }; SpriteObject::SpriteObject(const SpriteObject& obj) { texture = obj.texture; }Sprite
class Sprite : public SpriteObject { public: int volacity; Sprite(const Sprite& spt); }; Sprite::Sprite(const Sprite& spt):SpriteObject(spt) { this->volacity = spt.volacity; }Enemy
class Enemy : public Sprite { public: list<Shot*> shots; SpriteObject* ammo; Enemy(const Enemy& e); }; Enemy::Enemy(const Enemy& e):Sprite(e) { /*---Compiler Fehler bei Iterator Deklaration------ cannot convert from 'std::list<_Ty>::_Const_iterator<_Secure_validation>' to 'std::list<_Ty>::_Iterator<_Secure_validation>' */ list<Shot*>::iterator i = e.shots.begin(); for ( ; i != e.shots.end() ; ) { this->shots.push_back((*i)++); } }Vielen Dank für eure Bemühungen, bin für alle Vorschläge und Gedanken offen!
-
Wenn du in Konstruktoren Member initialisierst, solltest du die Initialisierungsliste verwenden. Das gilt auch für Kopierkonstruktoren. Also statt der Zuweisung wie hier:
SpriteObject::SpriteObject(const SpriteObject& obj) { texture = obj.texture; }machst du Folgendes:
SpriteObject::SpriteObject(const SpriteObject& obj) : texture(obj.texture) { }Aber wenn keine Deep-Copy nötig ist, also wenn nur Member für Member kopiert wird, kannst du die Implementierung auch weglassen, da reicht der compiler-generierte Kopierkonstruktor.
Zu deinem Iteratorproblem: Der Parameter des Kopierkonstruktors ist eine Const-Referenz auf
Enemy. Das bedeutet, dass du das referenzierte Objekt nicht verändern darfst. Du kannst also nicht mit einem normalen Iterator iterieren, da dieser die Sequenz manipulieren könnte. Stattdessen nimmst du einenconst_iterator, um nur lesend zuzugreifen.
-
1. Kommt darauf an, wie du kopierst. Wenn du ein Enemy Objekt kopierst, dann wird eine Kopie vom ganzen Enemy Objekt durchgeführt, also auch die Basisklassen. Wenn du eine Basisklasse kopierst, dann wird nur die Basisklasse und deren Basisklassen kopiert, also womöglich Enemy nicht korrekt und vollständig kopiert.
Falls du von der Basisklasse aus eine vollständige Kopie erreichen willst, kommst du um eine virtuellecloneFunktion nicht herum.
2. Die Frage ist, was ist dein Problem?
Zum Kopieren selber:
Du machst nichts anderes, als das was der Kompiler automatisch für dich implementieren würde. Ausser das du vergessen hast, auch die Munition zu kopieren, dass hätte der Kompiler nicht vergessen. Es ist allerdings fraglich, ob du wirklich dies haben willst. Denn so wie du es gemacht hast, werden sich zwei Enemy Objekte nach der Kopie ihre Textur und Schüsse Teilen, da sie jeweils Zeiger auf den gleichen Speicher besitzen.
Ich weiss nicht, wie ein Programm aufgebaut ist, aber oft will man sowas verhindern und jedes Objekt soll nach der Kopie auch intern seine eigenen Objekte haben. Das ist auch einer der Gründe, wieso man diese Konstruktoren selber implementieren möchte.
3.e(eine extrem vielsagende Variabel!) ist konstant, also iste.shotskonstant, also lieferte.shots.begin()einen konstanten Iterator, den du natürlich nicht einem nicht konstanten Iterator zuweisen kannst (Tipp:const_iterator).
4. http://de.wikipedia.org/wiki/Dreierregel_(C%2B%2B)
5. http://www.c-plusplus.net/forum/viewtopic-var-t-is-218760-and-highlight-is-swap.htmlGrüssli
-
Wenn du in Konstruktoren Member initialisierst, solltest du die Initialisierungsliste verwenden. Das gilt auch für Kopierkonstruktoren. Also statt der Zuweisung wie hier:
Danke, du hast natürlich recht. Das erspart mir pro Variable einen Defaultkonstruktoraufruf.
Du machst nichts anderes, als das was der Kompiler automatisch für dich implementieren würde. Ausser das du vergessen hast, auch die Munition zu kopieren, dass hätte der Kompiler nicht vergessen. Es ist allerdings fraglich, ob du wirklich dies haben willst. Denn so wie du es gemacht hast, werden sich zwei Enemy Objekte nach der Kopie ihre Textur und Schüsse Teilen, da sie jeweils Zeiger auf den gleichen Speicher besitzen.
Ahh, langsam kommt alles zu mir
ammo sollte jeweils eine eigene Textur haben, die korrekte Implementierung muss also im Enemy Kopierkonstruktor noch das enthalten:this->ammo = new SpriteObject(e.filename);Die Dreierregel war mir bekannt, aber ganz klar ist mir jetzt nicht wie der operator= funktioniert. Ich brauche eine deep copy. Aber im operator= darf ich keine Kopierkonstruktoren von Basisklassen aufrufen. Muss ich jetzt einfach alle Variablen der Basisklassen (per Setter) neu belegen?
Das funktioniert nämlich nicht, ich bekomme hier folgenden Fehler, aber nur wenn ich den Setter benutze!? Mache ich die Textur public, funktioniert die Geschichte. Kann mir darauf keinen Reim bilden, das ganze sollte ident sein.
(im Enemy operator=)this->texture = e.getTexture();1>c:\2dproject\2dproject\2dproject\enemy.cpp(138) : error C2662: 'SpriteObject::getTexture' : cannot convert 'this' pointer from 'const Enemy' to 'SpriteObject &'
Achja, eine praktische Frage für den (professionellen?) Alltag: Bei mir wachsen die Klassen ziemlich schnell, Enemy hat zb. über ein Dutzend Variablen zur Beschreibung seiner Attribute wie Schnelligkeit, Schusskraft usw usw. Aber ich hab mal von einer Regel gelesen die nur max. 7 Symbole pro Klasse erlaubt -> sonst ist sie unübersichtlich und überladen.
Aber in Computerspielen ist das doch kaum möglich: Wie gesagt muss ein Gegner allerhand an Informationen beinhalten. Die einzige Lösung scheint mir hier Enemy zb. noch von einer zweiten Klasse abzuleiten, nämlich irgendeine allgemeine "Unit" Klasse. Mehrfachvererbung ist aber ein ganz eigenes Problem..jemand eine Idee wie hier die "best practice" ist?Danke schonmal für eure super Hilfe !
-
FriedenEuchAllen schrieb:
Die Dreierregel war mir bekannt, aber ganz klar ist mir jetzt nicht wie der operator= funktioniert. Ich brauche eine deep copy. Aber im operator= darf ich keine Kopierkonstruktoren von Basisklassen aufrufen. Muss ich jetzt einfach alle Variablen der Basisklassen (per Setter) neu belegen?
Lies den Thread, welcher ich angegeben habe bei Punkt 5. Der sagt dir, wie du eine sehr schöne und Exception sichere Implementierung des
operator =machen kannst. Und gibt dir dazu auch bekannt, wie man es "traditionell" macht.FriedenEuchAllen schrieb:
Achja, eine praktische Frage für den (professionellen?) Alltag: Bei mir wachsen die Klassen ziemlich schnell, Enemy hat zb. über ein Dutzend Variablen zur Beschreibung seiner Attribute wie Schnelligkeit, Schusskraft usw usw. Aber ich hab mal von einer Regel gelesen die nur max. 7 Symbole pro Klasse erlaubt -> sonst ist sie unübersichtlich und überladen.
Das sind Definitionen die sich überall ein wenig unterscheiden, aber meistens eine gute Richtung angeben, lieber weniger als zuviel.
Um sowas zu erreichen muss man die Daten und Aufgaben aufteilen. Man kann grundsätzlich definieren, dass jede Klasse eine Aufgabe übernehmen soll, sobald sie mehrere Aufgaben übernimmt, sollte sie aufgeteilt werden.
Also überlege dir, was deine Klasse macht, sobald du ein "und" aussprichst, hast du ein gutes Zeichen dafür, dass eine Aufteilung womöglich nicht schlecht wäre
Sehr grundsätzliche Aufteilungen sind zudem schon mal:
Model <-> View <-> Controller
http://de.wikipedia.org/wiki/MVCMehrfachvererbung sind übrigens gar nicht so schlimm. Schau dir nur nochmals die Regeln in deinem lieblings C++ Buch oder Tutorial an, dann geht sowas ohne Probleme. Dies soll jetzt allerdings nur eine allgemeingültige Aussage sein und heisst nicht, dass Mehrfachvererbung die Lösung bei deinem Problem ist.

Grüssli
-
FriedenEuchAllen schrieb:
Achja, eine praktische Frage für den (professionellen?) Alltag: Bei mir wachsen die Klassen ziemlich schnell, Enemy hat zb. über ein Dutzend Variablen zur Beschreibung seiner Attribute wie Schnelligkeit, Schusskraft usw usw. Aber ich hab mal von einer Regel gelesen die nur max. 7 Symbole pro Klasse erlaubt -> sonst ist sie unübersichtlich und überladen.
Du musst dir überlegen, ob die einzelnen Attribute wirklich total unabhängig voneinander sind. Sonst könntest du sie in einzelne Klassen aufteilen, zum Beispiel:
struct Lage { Vektor Position; // eine Vektorklasse Vektor Geschwindigkeit; float Winkel; }; struct Bewaffnung { WaffenTyp Waffe; // ein Enum-Typ int Munition; float Feuerrate; };So kannst du immer weiter zusammenfassen, wodurch du schlussendlich doch auf recht wenige Member kommst. Aber von fixen Regeln wie "mehr als 7 Membervariablen sind schlecht" halte ich grundsätzlich nicht viel. Ach ja, ich würde den
structs noch Konstruktoren hinzufügen.