Operator= und Objektinitialisierung
-
hallo!
ich habe folgendes problem:
ich muss eine klasse "matrix" ersetzen, also selbst schreiben. das klappt auch ohne weiteres bis auf diesen aufrufmatrix m; m = matrix(10,10);
die lösung sollte in der überladung des '=' operators liegen, der original funktionskopf sieht so aus
matrix& operator=(const matrix&);
wie muss jedoch nun die funktion aussehen? ich habs mal so probiert, jedochj ohne erfolg...
matrix& matrix::operator=(const matrix& x) { return const_cast<matrix&>(x); }
ideen?
-
Dir fehlt ein Konstruktor
matrix(int x, int y) { ...whatever...}
und operator= sollte eine Referenz auf "this" zurückgeben.
-
Nimm dein Lieblings-C++-Buch zur Hand und guck unter «Zuweisungsoperator.»
-
CodeWalker schrieb:
Dir fehlt ein Konstruktor
matrix(int x, int y) { ...whatever...}
und operator= sollte eine Referenz auf "this" zurückgeben.
ja konstruktor und destruktor is natürlich vorhanden...
wie geb ich eine referenz auf this zurück?
und noch ne frage: ich wollte einen konstruktor ohne parameter einbauen und in diesem analog zu java mittels this(x,y) den konstruktor mit zwei parametern aufrufen, dies funktioniert jedoch nicht...
-
LoopyC schrieb:
wie geb ich eine referenz auf this zurück?
return *this;
Edit: *mist*
-
class matrix { public: matrix(int x=0, int y=0) //Default-Werte :mx(x), my(y) //Member-Variablen initialisieren { /* do whatever you want to do*/ }; virtual ~matrix(){}; virtual matrix& operator=(const matrix& rhs) { if (this!=&rhs) //prüfe auf Selbstzuweisung { this->mx=rhs.mx; //Werte kopieren this->my=rhs.my; }; return *this; //Referenz auf "this" zurückgeben }; protected: int mx; int my; }; int main(int argc, char* argv[]) { matrix m= matrix(10,10); matrix* pm = new matrix(); return 0; }
-
Kurze Anmerkungen:
class matrix { public: matrix(int x=0, int y=0) //Default-Werte :mx(x), my(y) //Member-Variablen initialisieren { /* do whatever you want to do*/ }; virtual ~matrix(){}; //Warum virtual?? virtual matrix& operator=(const matrix& rhs) //warum virtual?? { if (this!=&rhs) //prüfe auf Selbstzuweisung { this->mx=rhs.mx; //Werte kopieren this->my=rhs.my; //warum das this-> ? }; return *this; //Referenz auf "this" zurückgeben }; protected: //warum nicht private? int mx; int my; //warum m als prefix? my liest sich wie das englische my //und nicht wie member variable y //wenn schon eine kennzeichnung der membervariablen sein muss, dann doch //bitte eine gängigere nicht ganz so verwirrende }; int main(int argc, char* argv[]) { matrix m= matrix(10,10); matrix* pm = new matrix(); //wo ist das delete? return 0; }
-
@Shade: mal zur Info: das sollte nur ein Beispiel sein - oder sieht das für dich wie ein fertiges Programm aus?
Und wenn Du schon kritisierst, dann kritisiere auch alles: wenn man einen virtuellen Zuweisungsoperator implementiert, sollte man auch einen virtuellen Copy-Konstruktor implementieren.
Virtuell deshalb, weil diese Klasse nicht wie eine vollständige Klasse aussieht - deshalb ist davon auszugehen, daß sie als Basisklasse dient.
Das "this->" ist wirklich überflüssig - schadet IMHO aber auch nicht. Der Compiler ignoriert das eh.
Ob ich die Members protected oder private mache, hängt ja wohl von der weiteren Verwendung ab.
Und wenn wir schon Haare spalten wollen: das "delete" kann mich mir an dieser Stelle sparen, weil das Programm danach eh terminiert, gelle?
Und wie ich meine Membervariablen nenne, ist meine Sache, weil die zur Implementation gehören und nicht zur Schnittstelle.
-
CodeWalker schrieb:
Und wenn Du schon kritisierst, dann kritisiere auch alles: wenn man einen virtuellen Zuweisungsoperator implementiert, sollte man auch einen virtuellen Copy-Konstruktor implementieren.
Wie implementiert man einen virtuellen Copy Ctor?
als clone() Methode?Virtuell deshalb, weil diese Klasse nicht wie eine vollständige Klasse aussieht - deshalb ist davon auszugehen, daß sie als Basisklasse dient.
verstehe ich nicht. warum soll 'matrix' nicht ein value type sein?
Das "this->" ist wirklich überflüssig - schadet IMHO aber auch nicht. Der Compiler ignoriert das eh.
stört die lesbarkeit
Ob ich die Members protected oder private mache, hängt ja wohl von der weiteren Verwendung ab.
protected Member sind fast so schlimm wie public member.
nur das protected machen was sein muss - und das muss IMHO nicht sein.Und wenn wir schon Haare spalten wollen: das "delete" kann mich mir an dieser Stelle sparen, weil das Programm danach eh terminiert, gelle?
wieso? C++ hat keinen GC
Nur weil dein OS den Speicher freigibt heisst es nicht, dass das alle machen. OK blödes Argument
Aber jedes new muss sein delete haben - sonst entstehen irgendwann ressourcen löcher - wenn matrix() zB ebenfalls eine resource anfordert, zB ne DB Verbindung, File Lock, etc. Da kann das OS nicht alles wieder freigeben.Und wie ich meine Membervariablen nenne, ist meine Sache, weil die zur Implementation gehören und nicht zur Schnittstelle.
War ja nur n vorschlag. Warum nicht m_x oder wenigstens mX?
das problem ist, dass my einfach nicht wie "Membervariable y" aussieht, sondern wie das englische wort my - das verwirrt den Leser.
-
ok, streich bei "virtuellen Copy-Konstruktor" das "virtuellen" *seufz*
Is mir zu spät für solche Haarspaltereien
-
CodeWalker schrieb:
ok, streich bei "virtuellen Copy-Konstruktor" das "virtuellen" *seufz*
Dann streich doch den Copy-Konstruktor gleich mit
-
CodeWalker schrieb:
Und wenn Du schon kritisierst, dann kritisiere auch alles: wenn man einen virtuellen Zuweisungsoperator implementiert, sollte man auch einen virtuellen Copy-Konstruktor implementieren.
Quark, virtuelle Konstruktoren gibts nicht. Und virtuelle Zuweisungsoperatoren sind ein Kapitel für sich, ich würd mal unter Vorbehalt sagen, sie sind generell Schwachsinn. Wie soll man den denn einsetzen?
Fahrzeug *f = new Auto; Strassenbahn s; *f = s;
???
Was wenn die beiden unterschiedliche Größen haben? Zum Schluss Hand aufs Herz: Hast du sowas etwa schonmal gemacht und dabei den op= in der abgeleiteten Klasse überschrieben?Virtuell deshalb, weil diese Klasse nicht wie eine vollständige Klasse aussieht - deshalb ist davon auszugehen, daß sie als Basisklasse dient.
Vielleicht isses auch nur eine minimalst-Beispielklasse
-
hmmm...also folgendes Schema habe ich gelernt:
class OberKlasse { public: OberKlasse() { privateData_1=new int(0); privateData_2=new int(0); }; virtual ~OberKlasse() { delete privateData_1; delete privateData_2; }; virtual OberKlasse& operator=(const OberKlasse& rhs) { if (this!=&rhs) { localCopy(rhs); } return *this; } private: int* privateData_1; int* privateData_2; int localCopy(const OberKlasse& rhs) { *privateData_1=*rhs.privateData_1; *privateData_2=*rhs.privateData_2; } }; class Abgeleitet1 : public OberKlasse { public: Abgeleitet1() { privateData_3 = new int(0); privateData_4 = new int(0); }; virtual ~Abgeleitet1() { delete privateData_3; delete privateData_4; } Abgeleitet1& operator=(const OberKlasse& rhs) // <= hier verschrieben { const Abgeleitet1& rs = dynamic_cast<const Abgeleitet1&>(rhs); //<= und hier if (this!=&rhs) { OberKlasse::operator=(rs); localCopy(rs); }; return *this; } private: int* privateData_3; int* privateData_4; int localCopy(const Abgeleitet1& rhs) { *privateData_3=*rhs.privateData_3; *privateData_4=*rhs.privateData_4; } };
und dazu folgende Regeln:
1. Jede Klasse in einer Vererbungshierarchie soll die Zuweisung aller ihrer Attribute mit Ausnahme der geerbten in einer eigenen Methode kapseln. Diese Methode kann bei Einfachvererbung privat sein.2. Der Zuweisungsoperator muß virtual sein. Er ist verantwortlich für die zuweisung eines vollständigen Objekts der betreffenden Klasse und hat als Rückgabetyp eine Referenz dieser Klasse.
3. Der Zuweisungsoperator einer abgeleiteten Klasse, egal an welcher Stelle der Vererbungshierarchie, hat die oben angegebene Struktur, wobei die abgeleitete Klasse von der direkten Oberklasse abgeleitet wird.
siehe auch: U. Breymann, C++ Eine Einführung, 5. Auflage 1999, pg. 336 ff.
Naja, vielleicht ist das ja inzwischen überholt? *grübel*
[edit] hab mich oben verschrieben - is schon spät
[/edit]
-
CodeWalker schrieb:
also folgendes Schema habe ich gelernt:
Wo lernt man sowas?
Bei mir nicht...1. Jede Klasse in einer Vererbungshierarchie soll die Zuweisung aller ihrer Attribute mit Ausnahme der geerbten in einer eigenen Methode kapseln. Diese Methode kann bei Einfachvererbung privat sein.
Zuweisung? Also der op=?
Ob der private ist oder nicht, hängt nicht davon ab ob die Klasse in einer Hierachie aufscheint, sondern einfach davon, ob man das Objekt kopieren darf.2. Der Zuweisungsoperator muß virtual sein. Er ist verantwortlich für die zuweisung eines vollständigen Objekts der betreffenden Klasse und hat als Rückgabetyp eine Referenz dieser Klasse.
Ich kenne mich mit den Problemen von virtuellen op= nicht aus - deswegen habe ich auch nichts dazu geschrieben - aber ich habe das noch nie gebraucht, ich wüsste nicht einmal wo es Sinn machen würde...
3. Der Zuweisungsoperator einer abgeleiteten Klasse, egal an welcher Stelle der Vererbungshierarchie, hat die oben angegebene Struktur, wobei die abgeleitete Klasse von der direkten Oberklasse abgeleitet wird.
Den Satz verstehe ich nicht.
siehe auch: U. Breymann, C++ Eine Einführung, 5. Auflage 1999, pg. 336 ff.
Ich hab das Buch leider nicht
class OberKlasse { public: OberKlasse() { //Initliste! privateData_1=new int(0); privateData_2=new int(0); //nicht exception safe }; virtual ~OberKlasse() { delete privateData_1; delete privateData_2; }; virtual OberKlasse& operator=(const OberKlasse& rhs) { if (this!=&rhs) { localCopy(rhs); } return *this; } //op= lehre ich so: OberKlasse& operator=(OberKlasse other) { swap(other); } //wobei dies einen CopyCtor erforder //swap sieht dann so aus: void swap(OberKlasse& other) { std::swap(privateData_1, other.privateData_1); std::swap(privateData_2, other.privateData_2); } private: int* privateData_1; int* privateData_2; int localCopy(const OberKlasse& rhs) { //das int übersehe ich mal ;) *privateData_1=*rhs.privateData_1; *privateData_2=*rhs.privateData_2; //nicht exception safe } }; class Abgeleitet1 : public OberKlasse { public: Abgeleitet1() { //Initliste! privateData_3 = new int(0); privateData_4 = new int(0); //nicht exception safe }; virtual ~Abgeleitet1() { delete privateData_3; delete privateData_4; } Abgeleitet1& operator=(const Abgeleitet1& rhs) { const OberKlasse& rs = dynamic_cast<const OberKlasse&>(rhs); //ein dynamic_cast? sinnlose kosten if (this!=&rhs) { OberKlasse::operator=(rs); localCopy(rhs); //nicht exception safe }; return *this; } private: int* privateData_3; int* privateData_4; int localCopy(const Abgeleitet1& rhs) { *privateData_3=*rhs.privateData_3; *privateData_4=*rhs.privateData_4; //nicht exception safe } };
btw: der Test auf selbstzuweisung ist nicht ganz unproblematisch. ich würde ihn deshalb lieber weglassen. zumal er ja nix bringt. denn wenn jemand
x=x; schreibt, wird er damit leben müssen dass x sich selbst kopiert.
-
lassen wir mal diesen lästigen selbstzuweisungstest weg (ist in dem fall egal):
class base { int foo_; public: base (int i) : foo_(i) {} int foo () const { return foo_; } virtual base &operator = (const base &b) { foo_ = b.foo_; return *this; } virtual ~base () {} }; class derived : public base { int own_foo_; public: derived (int a, int b) : base(a), own_foo_(b) {} int own_foo () const { return own_foo_; } derived &operator = (const derived &d) { const base& b = dynamic_cast<const base&>(d); //warum so umständlich? base::operator=(b); own_foo_ = d.own_foo_; return *this; } }; int main() { derived d(1,2); base *b = new derived(0,0); *b = d; //das sollte ja wohl das anwendungsgebiet sein... cout << b->foo() << " und " << static_cast<derived*>(b)->own_foo() << endl; delete b; }
und, was liefert die ausgabe?
deklariere den operator= in base einmal als rein virtuell und schau dir die fehlermeldung deines compilers an.
-
Shade Of Mine schrieb:
btw: der Test auf selbstzuweisung ist nicht ganz unproblematisch. ich würde ihn deshalb lieber weglassen. zumal er ja nix bringt. denn wenn jemand
x=x; schreibt, wird er damit leben müssen dass x sich selbst kopiert.Das eigentliche Problem mit der Selbstzuweisung ist ein anderes.
MyClass& operator=(const MyClass& other) { delete bla; bla = new ... // Initialisiert mit dem Wert von other. Aber hey, den Wert haben wir gerade gelöscht! }
Und was gibt es an dem Adressenvergleich auszusetzen? Ich sehe kein Problem.
CodeWalker:
Wozu man allerdings nen virtuellen Zuweisungsoperator braucht, entzieht sich meiner Kenntnis. AFAIK implementiert der Compiler sowieso automatisch für jede Klasse einen Zuweisungsoperator, den man halt bei Bedarf redefinieren kann. Und wenn man ihn redefiniert, sollte man natürlich auch den operator= der Basisklasse aufrufen.
-
Optimizer schrieb:
Und was gibt es an dem Adressenvergleich auszusetzen? Ich sehe kein Problem.
Ich muss gestehen, dass ich es vergessen habe. Da war irgendwas mit Basisklasse und sonstwas.
Sorry - ich weiss es nicht mehr.
Einfach meine exception safe Variante nehmen und man hat das Problem nicht mehr.
Ausserdem ist meine Variante so schön kurz
-
Das Problem ist, daß Zuweisungsoperatoren, die um korrekt zu sein auf Selbstzuweisung testen müssen meist nicht stark exception sicher sind.
MfG Jester
-
Shade Of Mine schrieb:
Optimizer schrieb:
Und was gibt es an dem Adressenvergleich auszusetzen? Ich sehe kein Problem.
Ich muss gestehen, dass ich es vergessen habe. Da war irgendwas mit Basisklasse und sonstwas.
Sorry - ich weiss es nicht mehr.
Scott Meyers schreibt sowas in einem seiner "Effective C++" Bücher. Er irrt sich damit aber. Der Code den er da präsentiert macht dem Vergleich keinerlei Probleme.
Sowohl und mehrfacher, als auch unter virtueller Vererbung macht der Test, dass was er soll.Unter mehrfacher (nicht virtueller) Vererbung kann er allerdings zu auf den ersten Blick merkwürdigen Ergebnissen kommen:
class A { public: A& operator = (const A& rhs) { if(this!=&rhs) cout << "ungleich" << endl; else cout << "gleich" << endl; return *this; } }; class B: public A { }; class C: public A { }; class D: public B, public C { }; int main() { D d; A& aOverB = static_cast<B&>(d); A& aOverC = static_cast<C&>(d); aOverB = aOverC; }
Hier erhält man als Ausgabe "ungleich", obwohl doch letztlich nur ein einziges D-Objekt verglichen wird. Genauer betrachtet macht das aber sinn. d enthält zwei A-Objekte, die natürlich zwei unterschiedliche Adressen besitzen und genau die Adressen dieser beiden unterschiedlichen A-Objekte vergleichen wir.
Jesters Punkt ist der Entscheidene.
-
also der aufruf
matrix m = matrix(10,10);
funktioniert jetzt, nicht jedoch
matrix m; m = matrix(10,10);
edit: jetzt läufts, hatte nen syntax-fehler bei der überladung des konstruktors...