kopie einer referenz?
-
Nexus schrieb:
Nein. Standardmässig kopiert er memberweise, also nur die Zeiger selber.
Ok...verstehe ich das richtig, wenn ich weder Kopierkonstrukter noch Zuweisungsoperator explizit implementiere, werden beide vom Compiler erstellt und tun das gleiche, nämlich alle Membervariablen kopieren? Und falls ja, werden die Member dann wiederum mit ihrem eigenen Kopierkonstruktor kopiert, oder mit ihrem eigenen Zuweisungsoperator?
-
Hackfresser schrieb:
Ok...verstehe ich das richtig, wenn ich weder Kopierkonstrukter noch Zuweisungsoperator explizit implementiere, werden beide vom Compiler erstellt und tun das gleiche, nämlich alle Membervariablen kopieren?
Ja, sofern das geht. Hat man Referenzen oder konstante Objekte als Member, kann kein Zuweisungsoperator generiert werden. Hat man nichtkopierbare Objekte als Member, kann zusätzlich kein Kopierkonstruktor generiert werden.
Hackfresser schrieb:
Und falls ja, werden die Member dann wiederum mit ihrem eigenen Kopierkonstruktor kopiert, oder mit ihrem eigenen Zuweisungsoperator?
Ja. PODs haben zwar keine Konstruktoren, werden also bitweise kopiert.
-
Nexus schrieb:
Ja, sofern das geht. Hat man Referenzen oder konstante Objekte als Member, kann kein Zuweisungsoperator generiert werden.
Hm... Ich hab mir mal ne Klasse mit Referenzen und konstanten Objekten als Member geschrieben, und da scheint der Zuweisungsoperator einwandfrei zu funktionieren, also:
class CTest { public: CTest (int a, MyInt b, MyInt& c) : _a(a), _b(b), _c(c){}; //... const int _a; const MyInt _b; MyInt& _c; };In der main kann ich dann problemlos sowas machen:
MyInt ref(3); CTest t1(1, MyInt(2), ref); CTest t2 = t1;Nexus schrieb:
Hat man nichtkopierbare Objekte als Member, kann zusätzlich kein Kopierkonstruktor generiert werden.
Wann ist denn ein Objekt nicht kopierbar? Wir hatten in der VL ein Entwurfsmuster namens "Singleton", da wurde dann Kopierkonstruktor und Zuweisungsoperator privat deklariert, aber nicht implementiert, aber gibt es noch andere Möglichkeiten?
Nexus schrieb:
Hackfresser schrieb:
Und falls ja, werden die Member dann wiederum mit ihrem eigenen Kopierkonstruktor kopiert, oder mit ihrem eigenen Zuweisungsoperator?
Ja.
Ja, Kopierkonstruktor, oder Ja, Zuweisungsoperator?
-
powiwe schrieb:
Hackfresser schrieb:
Wenn ich versuche, m_feld in ein array zu ändern, also
private: T m_feld[Max];...bekomme ich im Konstruktor bei der Zeile
m_feld = new T[Max];die Fehlermeldung
error C2440: '=' : cannot convert from 'int *' to 'int [20]'
Warum geht das nicht?
weil ein array T[Max] nun mal kein zeiger ist.
Ich verstehe es immer noch nicht. Ich kann mir ein array machen:
int a[3] = {1,2,3};dann ist doch a ein Zeiger auf das erste Element?
cout << a;gibt mir doch ne Adresse aus, genau wie auch
int* b = new int; cout << b;weiterhin kann ich mit a rechnen wie mit einem Zeiger, und auch b kann ich ansprechen wie ein array:
*(b+1) = 20; cout << *(a+1); // 2 cout << b[1]; // 20Hat es vielleicht etwas mit dynamischem und automatischem Speicher zu tun?
Und wieso kann ich für die Grösse eines arrays keine Variable benutzen?int a = 3; int b[a] // FehlerIm dynamischen Speicher ist das ja kein Problem:
int* d = new int[a];
-
Hackfresser schrieb:
In der main kann ich dann problemlos sowas machen:
MyInt ref(3); CTest t1(1, MyInt(2), ref); CTest t2 = t1;Die letzte Zeile ist eine Initialisierung, keine Zuweisung.
Hackfresser schrieb:
Ich verstehe es immer noch nicht. Ich kann mir ein array machen:
int a[3] = {1,2,3};dann ist doch a ein Zeiger auf das erste Element?
Nein. a ist der Name des Arrays. Der ist implizit in einen rvalue-int-Zeiger konvertierbar, der auf das erste Element zeigt.
-
Hackfresser schrieb:
Ich verstehe es immer noch nicht. Ich kann mir ein array machen:
int a[3] = {1,2,3};dann ist doch a ein Zeiger auf das erste Element?
Nein,
aist der Name des Arrays.
Der Unterschied wird auch so ersichtlich:int a[3] = {1,2,3}; std::cout << sizeof(a) << '\n' //gibt 3 * sizeof(int) aus (vermutlich 12) int* pa = a; //hier wird a implizit in einen Pointer konvertiert std::cout << sizeof(pa) << '\n' //gibt die groesse von int* aus (vermutlich 4) ++a; //Fehler, man kann keine Pointerarithmetik mit Arrays machen, da Arrays keine Pointer sind ++pa; //geht -> Pointerarithmetikcout << a;gibt mir doch ne Adresse aus, genau wie auch...
Ja, weils implizit in einen Pointer konvertiert wird.
int* b = new int; cout << b;Hier ist b wirklich ein Pointer.
weiterhin kann ich mit a rechnen wie mit einem Zeiger, und auch b kann ich ansprechen wie ein array:
cout << *(a+1); // 2Dabei wird ein temporärer Pointer erstellt, dieser wird um 1 erhöht und dann dereferenziert. a bleibt aber weiterhin das Array.
`a = a + 1; //oder ++a -> FEHLERstd::cout << *a
`
geht hingegen nicht.
-
Hackfresser schrieb:
In der main kann ich dann problemlos sowas machen:
MyInt ref(3); CTest t1(1, MyInt(2), ref); CTest t2 = t1;Das liegt daran, dass wenn der Zuweisungsoperator in der Deklaration vorkommt, es nicht wirklich der Zuweisungsoperator, sondern der Kopierkonstruktor ist, der aufgerufen wird.
CTest t2(t1);wäre das Gleiche. Die Schreibweise mit = geht auch nur bei nicht expliziten Konstruktoren.
Hackfresser schrieb:
Ja, Kopierkonstruktor, oder Ja, Zuweisungsoperator?
Beim standardgenerierten Kopierkonstruktor werden die Member mit ihrem Kopierkonstruktor kopiert, beim Zuweisungsoperator werden sie mit ihrem Zuweisungsoperator zugewiesen.
-
Vorweg erstmal Danke für die hilfreichen Antworten.
Dann noch ein paar Fragen:Hat man Referenzen oder konstante Objekte als Member, kann kein Zuweisungsoperator generiert werden.
Ok... aber warum ist das so?
Und überhaupt, warum gibts bei C++ die Unterscheidung von Kopie und Zuweisung?
Mal ganz naiv: bei den Übungsprogrämmchen die ich bisher schreiben musste war die Funktion von Kopierktor und Zuweisung immer gleich, soweit ich das überblicken kannOffen ist auch noch meine Frage, warum man Arrays auf dem Stack nicht mit variabler Grösse initialisieren kann...
Ausserdem: von Java bin ich es gewohnt, Objekte deklarieren zu können, ohne sie Initialisieren zu müssen, bzw sie werden auf NULL initialisiert, also:
MeineKlasse instanz;(Ich hoffe das stimmt so, Java ist für mich laange her.)
Wenn ich das bei C++ versuche:CMeineKlasse instanz;...versucht er immer gleich, den default-Ktor aufzurufen? Warum ist das so?
-
Hackfresser schrieb:
Hat man Referenzen oder konstante Objekte als Member, kann kein Zuweisungsoperator generiert werden.
Ok... aber warum ist das so?
Wenn du eine Klasse mit konstanten Membern (Referenzen sind immer konstant) zuweisen willst, müssen ja alle Member zugewiesen werden. Da einem konstanten Wert per Definition nichts zugewiesen werden kann, ist die Zuweisung der gesamten Klasse nicht möglich (zumindest beim standardgenerierten
operator=).Hackfresser schrieb:
Und überhaupt, warum gibts bei C++ die Unterscheidung von Kopie und Zuweisung?
Mal ganz naiv: bei den Übungsprogrämmchen die ich bisher schreiben musste war die Funktion von Kopierktor und Zuweisung immer gleich, soweit ich das überblicken kannKopie beinhaltet die Erstellung eines neuen Objekts. Das Objekt kann gerade mit den kopierten Werten initialisiert werden. Zuweisung hingegen verändert den Status eines bereits bestehenden Objekts.
Hackfresser schrieb:
Offen ist auch noch meine Frage, warum man Arrays auf dem Stack nicht mit variabler Grösse initialisieren kann...
C++ hat dafür diesen Weg eingelegt. Möglich wäre es (->
alloca()), aber es ist auch meist sinnvoll, dafür den Heap zu benutzen. Vor allem weil dynamische Arrays oft grösser werden können und der Stack in seiner Grösse beschränkt ist. Aber mit der STL kann man dynamische Container sowieso viel komfortabler als jede Form von rohen Arrays benutzen, von daher ist die Einschränkung ertragbar.Hackfresser schrieb:
...versucht er immer gleich, den default-Ktor aufzurufen? Warum ist das so?
Das ist auch eine Eigenschaft von C++. Willst du nur einen Verweis auf ein Objekt anlegen und das später initialisieren, kannst du Zeiger benutzen:
CMeineKlasse* Zeiger = 0; // zur Sicherheit mit Null initialisieren, um Ungültigkeit kennzuzeichnen // ... mache etwas Zeiger = new CMeineKlasse(/* Konstruktorargumente */); // ... verwende Zeiger delete Zeiger;
-
Sehr gut, danke Leute!
Ich habe mit dem, was Ihr mir alles erklärt habt sogar meine ursprüngliche Frage selbst beantworten können.
-
Hackfresser schrieb:
Ausserdem: von Java bin ich es gewohnt, Objekte deklarieren zu können, ohne sie Initialisieren zu müssen, bzw sie werden auf NULL initialisiert, also:
In C++ gibt es eigentlich keine ungültige Objekte. Das, was am nächsten kommt ist der Zeiger, der 0 ist. Das ist allerdings ein gültiger Zeiger. Er zeigt auf kein Objekt, aber das Objekt ansich ist gültig. Die "Ungültigkeit" kommt vom Betrachter her, der dann sagt, dass man an eine Funktion keinen 0-Zeiger übergeben darf oder wie auch immer. Aber man kann durchaus auch mit 0 als Zeiger rechnen. Insbesondere Klasse Instanzen haben keinen Zustand, der mit NULL beschrieben werden kann. Die Ungültigkeit kommt auch hier wieder vom Programmierer aus, der sagt, dass eine bestimmte Klasse eine Invariante haben muss und ansonsten ist sie ungültig. (Und wirft allenfalls eine exception).