Hallo zusammen.
Ich habe sagen wir eine Klasse Matrix, deren Konstruktor Speicher für
eine matrix mit Länge*Breite erzeugt.Beim Beenden wird der Destruktor
aufgerufen und löscht mir diesen Speicher.Soweit so gut.
Wenn ich nun in main.cpp 2 Instanzen erzeuge und zuweise
"Klasse1=Klasse2"
dann wird der Destruktor ja 2 mal aufgerufen, löscht aber nur einen
Speicher(aufgrund der Zuweisung). Der andere Speicher wird vom Heap
nicht mehr gelöscht.
Meine Frage ist nun, was es in C++ für Möglichkeiten gibt, soetwas zu
verhindern.(Mir fällt nichts ein.)
Danke.
Der Code:
Christian schrieb:> Meine Frage ist nun, was es in C++ für Möglichkeiten gibt, soetwas zu> verhindern.(Mir fällt nichts ein.)
du muss dafür sorgen, das beim Kopieren von Objekte ein neuer Zeiger
angelegt wird.
Dafür muss du den Zuweisungs und Kopie Operator überschreiben. Dann
funktioniert auch das freigeben vom Speicher.
Christian schrieb:> Wenn ich nun in main.cpp 2 Instanzen erzeuge und zuweise> "Klasse1=Klasse2"> dann wird
dir der Compiler erst einmal erzählen, daß er dafür einen von dir
beigstellten Copy-Operator benötigt, weil er den default-Operator
vorsichtshalber gelöscht hat. Beim gcc klingt das so:
>C:\pocketcpp\VectorTest\Matrix.cpp:27:11: error: use of deleted function >'Matrix& Matrix::operator=(const Matrix&)'> cMatrix1 = cMatrix3;
^
>C:\pocketcpp\VectorTest\Matrix.cpp:4:7: note: 'Matrix& Matrix::operator=>>(const Matrix&)' is implicitly deleted because the default definition would be >ill-formed:
Der Rest liegt dann in deiner Hand.
Merkwürdig, VisualStudio Compiler lässt es aber mit sich machen, noch
nicht mal mit Warnung. Es erscheint nur eine "Debug assertion failed"
Meldung.
Gruss
Christian
Christian schrieb:> Merkwürdig, VisualStudio Compiler lässt es aber mit sich machen, noch> nicht mal mit Warnung. Es erscheint nur eine "Debug assertion failed"> Meldung.
warum soll es das nicht machen? Es ist ja kein Fehler vorhanden.
Christian schrieb:> Merkwürdig, VisualStudio Compiler lässt es aber mit sich machen, noch> nicht mal mit Warnung.
Das ist ein Service vom gcc, das er das macht, indem er den/die
Konstruktor/en analysiert.
Das machen längst nicht alle Compiler. Als Programmierer muss man eben
wissen, was man tut.
Konkret ist das Vorgehen als die "Rule of 3" bekannt:
Benötigt eine Klasse eine der 3 speziellen Member Funktionen
* Destruktor
* Copy Konstruktor
* Zuweisungsoperator
in einer programmierer-definierten Version, dann benötigt sie sehr
wahrscheinlich alle 3 programmierer-definiert.
Die Rule of 3 kommt daher, weil der Compiler diese 3 Member Funktionen
selbst erzeugt, wenn der Programmierer keine schreibt. Ist es für die
Klasse notwendig, eine der 3 Member Funktionen selbst zu schreiben, weil
die automatisch generierte falsch wäre, dann sind die anderen beiden
meistens ebenfalls falsch.
Edit:
Sehe gerade, dass Tom K. die 'Rule of Three' schon verlinkt hatte.
// Inhalt von cMatrix3 nach cMatrix4 verschieben; cMatrix3 ist danach leer (schnell)
22
MatrixcMatrix4(std::move(cMatrix3));
23
24
}
Außerdem gibts in C++ keinen heap; nur Free Storage und Automatic
Storage. Die Variablen cMatrix1,3,4 und m_nCols, m_nRows, m_values sind
alle im Automatic Storage (sie werden automatisch gelöscht), und der
Ziel-Speicher von m_values ist im Free Storage (und muss daher manuell
mit delete gelöscht werden).
Wenn es auf einem Ziel-Computer einen heap gibt und der Compiler/stdlib
den mit "new" angeforderten Speicher auf diesen heap packt, können
natürlich auch m_nCols, m_nRows auf dem Heap sein, wenn man nämlich "new
Matrix" macht. Daher kann man nie so wirklich sagen ob eine Variable auf
dem heap ist oder nicht; ob eine Variable im Automatic oder Free Store
ist allerdings schon.
Karl Heinz schrieb:> Christian schrieb:>> Merkwürdig, VisualStudio Compiler lässt es aber mit sich machen, noch>> nicht mal mit Warnung.>> Das ist ein Service vom gcc, das er das macht, indem er den/die> Konstruktor/en analysiert.
Und von allen Standard C++ konformen Compilern.
> Das machen längst nicht alle Compiler. Als Programmierer muss man eben> wissen, was man tut.
Dann sind diese Compiler nicht Standard-konform.
Dr. Sommer schrieb:> Karl Heinz schrieb:>> Christian schrieb:>>> Merkwürdig, VisualStudio Compiler lässt es aber mit sich machen, noch>>> nicht mal mit Warnung.>>>> Das ist ein Service vom gcc, das er das macht, indem er den/die>> Konstruktor/en analysiert.> Und von allen Standard C++ konformen Compilern.
Verzeih. Ich bin da schon einige Zeit nicht mehr auf dem laufenden.
Wird das von einem der letzten Standards gefordert?
Das hab ich verstanden.
Erkenne aber nicht wirklich einen Unterschied zwischen Heap und
Free/Automatic Storage.
cMatrix1,2,3,4 werden über Konstruktor aufgerufen und deswegen
automatic, oder?
Wann ist free, wann automatic?
Christian
Karl Heinz schrieb:> Verzeih. Ich bin da schon einige Zeit nicht mehr auf dem laufenden.> Wird das von einem der letzten Standards gefordert?http://en.cppreference.com/w/cpp/language/copy_constructor - "If no
user-defined copy constructors are provided for a class type (struct,
class, or union), the compiler will always declare a copy constructor as
an inline public member of its class."
Das ist aber mindestens seit C++98 schon so...
Christian schrieb:> Das hab ich verstanden.> Erkenne aber nicht wirklich einen Unterschied zwischen Heap und> Free/Automatic Storage.
Free/Automatic Storage sind die abstrakten Begriffe aus dem C++
Standard, die für den C++ Programmierer relevant sind. Heap und Stack
hingegen sind für Compiler/stdlib-Autoren interessant.
> cMatrix1,2,3,4 werden über Konstruktor aufgerufen und deswegen> automatic, oder?
Würde ich "new Matrix (3, 4);" schreiben würde da natürlich auch der
Konstruktur aufgerufen, aber die Matrix-Instanz liegt dennoch im Free
Store.
Alle Variablen selber sind in Automatic Store; die Ziele von Pointern,
wenn sie aus "new" gefüllt werden, sind im Free Store.
Dr. Sommer schrieb:> Karl Heinz schrieb:>> Verzeih. Ich bin da schon einige Zeit nicht mehr auf dem laufenden.>> Wird das von einem der letzten Standards gefordert?>> http://en.cppreference.com/w/cpp/language/copy_constructor - "If no> user-defined copy constructors are provided for a class type (struct,> class, or union), the compiler will always declare a copy constructor as> an inline public member of its class."
Ja. Sag ich doch.
Für mich hat deine Antwort jetzt so geklungen, als ob sich da in den
letzten Standards was geändert hätte.
Denn das hier
ich
>> Das ist ein Service vom gcc, das er das macht,>> indem er den/die Konstruktor/en analysiert.
du
> Und von allen Standard C++ konformen Compilern.
folgt ja gerade nicht. Ob der COmpiler die Konstruktoren analysiert oder
nicht, hat nichts damit zu tun, ob er standard-konform ist oder nicht.
Laut C++ Standard ist kein Compiler dieser Welt dazu verpflichtet,
problamtische Rule of 3(5) Velretzungen zu erkennen bzw. Vorkehrungen zu
treffen, wie dann eben keinen Copy Konstruktor oder Assignment Op
automatisch zu generieren.
Oder reden wir da jetzt gerade aneinander vorbei?
Funktioniert mit überschriebenen Copy Konstruktor:
Matrix( const Matrix & original )
{
m_values = new _T [original.m_rows*original.m_columns];
//operator=(original);
}
Muss aber Instanz so anlegen:
Matrix cMatrix2=cMatrix1;
... sonst wird der Kopy Konstruktor ja nicht aufgerufen und das Problem
bleibt.
Meine Frage noch:
Wenn ich die Zeile //operator=(original) auskommentiere verhält es sich
wie ohne Kopy Konstruktor, d.h. die debug assertion Meldung kommt
wieder.
Was passiert da?
Dr. Sommer schrieb:> http://en.cppreference.com/w/cpp/language/copy_constructor - "If no> user-defined copy constructors are provided for a class type (struct,> class, or union), the compiler will always declare a copy constructor as> an inline public member of its class.">> Das ist aber mindestens seit C++98 schon so...
und was hat das mit dem Problem zu tun?
Dort steht doch das er selber ein copy_constructor anlegt und das macht
der MS doch? Dann müsste der GCC sogar fehlerhaft sein, denn er macht es
ja scheinbar nicht.
Oder lese ich den text falsch?
Mir noch aufgefallen:
Dr. Sommer schrieb:
//ruft Konstruktor auf und reserviert heap1
Matrix cMatrix1;
//ruft Konstruktor auf und reserviert heap2
Matrix cMatrix3;
// Kopie anlegen (langsam)
cMatrix3 = cMatrix1;
Die Kopie wird hier nicht angelegt. Es bleibt dabei, das der Default
Operator aufgerufen wird, nicht der Copy.
Erst wenn:
Matrix cMatrix3 = cMatrix1;
...wird Copy aufgerufen.
Christian schrieb:> Was passiert da?
Ich schätze mal, das dein Verständnis davon, wann ein Copy Konstrultor
und wann ein Zuweisungsoperator benutzt wirf fehlerhaft ist.
Ein Konstruktor (also auch ein Copy Konstruktor), wird immer dann
benutzt, wenn ein neues Objekt erzeugt wird.
In
1
MatrixcMatrix2=cMatrix1;
wird ein neues Objekt erzeugt, nämlich cMatrix2.
Objekterzeugung ist IMMER ein Aufruf eines Konstruktors
Hier hingegen
1
MatrixcMatrix2;
2
3
cMatrix2=cMatrix1;
existiert das Objekt cMatrix2 schon, ehe das Programm zur
problematischen Stelle kommt. Das = ist hier also kein Konstruktor
Aufruf, sondern der Aufruf eines Zuweisungsoperators.
Du musst
* Copy Konstruktor
UND
* Zuweisungsoperator
überschreiben!
Die beiden haben auch verschiedene Aufgaben.
Ein Konstruktor erzeugt ein neues Objekt aus dem nichts. Ein
Zuweisungsoperator arbeitet aber bereits auf einem gültigen Objekt. D.h.
unter Umständen muss der im Objekt allokierte Resourcen freigeben, ehe
er dann die für die Zuweisung notwendige Semantik durchführt!
Das ist ein wesentlicher Unterschied! Und daher ist es in C++ wichtig,
zwischen Initialisierung und Zuweisung zu unterscheiden. Initialisierung
mündet in einem Konstruktoraufruf und findet statt/kann nur statt
finden, wenn ein NEUES Objekt erzeugt wird. Zuweisung hingegen operiert
immer auf einem bereits existierendem Objekt.
> Für mich hat deine Antwort jetzt so geklungen, als ob sich da in den> letzten Standards was geändert hätte.
Deine klang so als würde nur der GCC die default Funktionen erzeugen :o)
> folgt ja gerade nicht. Ob der COmpiler die Konstruktoren analysiert oder> nicht, hat nichts damit zu tun, ob er standard-konform ist oder nicht.
Das muss er aber, um den automatischen Konstruktor anzulegen bzw. nicht
anzulegen.
> Laut C++ Standard ist kein Compiler dieser Welt dazu verpflichtet,> problamtische Rule of 3(5) Velretzungen zu erkennen bzw. Vorkehrungen zu> treffen, wie dann eben keinen Copy Konstruktor oder Assignment Op> automatisch zu generieren.
Ja, beim Copy-Constructor nicht. Aber der Default Constructor wird zB
nicht angelegt wenn ein selbst definierter Konstruktor da ist...
Christian schrieb:> m_values = new _T [original.m_rows*original.m_columns];
Bitte keinen Bezeichner _T verwenden, das wird vom C++ Standard
verboten!!!
http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier/228797#228797Peter II schrieb:> Dort steht doch das er selber ein copy_constructor anlegt und das macht> der MS doch? Dann müsste der GCC sogar fehlerhaft sein, denn er macht es> ja scheinbar nicht.
Der GCC macht das auch, aber der default copy constructor macht nicht
das was der TO braucht...
Christian schrieb:> Die Kopie wird hier nicht angelegt. Es bleibt dabei, das der Default> Operator aufgerufen wird, nicht der Copy.
Ja, deswegen habe ich auch den copy assignment operator definiert, weil
der hier aufgerufen wird...
Dr. Sommer schrieb:> http://en.cppreference.com/w/cpp/language/copy_constructor - "If no> user-defined copy constructors are provided for a class type (struct,> class, or union), the compiler will always declare a copy constructor as> an inline public member of its class.">> Das ist aber mindestens seit C++98 schon so...
Das war es auch schon vorher, aber was hat das mit Warnungen zu tun,
wenn ich es nicht selbst definiere?
Klaus Wachtler schrieb:> Das war es auch schon vorher, aber was hat das mit Warnungen zu tun,> wenn ich es nicht selbst definiere?
Nix, Warnungen sind Compiler-Sache
Dr. Sommer schrieb:>> Für mich hat deine Antwort jetzt so geklungen, als ob sich da in den>> letzten Standards was geändert hätte.> Deine klang so als würde nur der GCC die default Funktionen erzeugen :o)
Dann haben wir aneinander vorbei geredet und reden ohnehin vom selben.
Meine Aussage bezog sich auf Olivers Aussage von 11:19 bzw. Christians
Erwiderung (11:27) darauf.
Notiz an mich: klarer darstellen, worauf sich Antworten beziehen und wie
die Posting-Kette dazu war.
Karl Heinz schrieb:> Notiz an mich: klarer darstellen, worauf sich Antworten beziehen und wie> die Posting-Kette dazu war.
Wir brauchen ein Forum mit Graphenstruktur - die Posts werden als Knoten
in einem gerichteten Graphen dargestellt, mit Kanten als "Antwort-Auf"
Relation :o)