C ++ - Objekt als Rückgabewert: Kopieren oder Referenzieren?

7

Ich wollte testen, wie sich C ++ verhält, wenn der Rückgabewert einer Funktion ein Objekt ist. Ich habe dieses kleine Beispiel gemacht, um zu beobachten, wie viele Bytes zugeordnet sind und festzustellen, ob der Compiler eine Kopie des Objekts erstellt (wie wenn ein Objekt als Parameter übergeben wird) oder stattdessen eine Art von Referenz zurückgibt.

Allerdings konnte ich dieses sehr einfache Programm nicht ausführen und ich habe keine Ahnung warum. Fehler sagt: "Debug-Assertion fehlgeschlagen! Ausdruck: BLOCK_TYPE_IS_INVALID" in einigen dbgdel.cpp-Datei. Project ist eine Win32-Konsolenanwendung. Aber ich bin mir ziemlich sicher, dass etwas mit diesem Code nicht stimmt.

%Vor%     
user1316208 05.04.2012, 20:27
quelle

4 Antworten

12

Sie haben die Dreierregel verletzt.

Wenn Sie ein Objekt zurückgeben, wird eine Kopie erstellt und anschließend zerstört. Sie haben also eine Abfolge von Ereignissen wie

%Vor%

Das heißt, zwei Objekte werden erstellt: Ihre ursprüngliche Objektkonstruktion, gefolgt vom impliziten Kopierkonstruktor. Dann werden beide Objekte gelöscht.

Da beide Objekte den selben -Zeiger enthalten, rufen Sie delete zweimal für denselben Wert auf. BOOM

Extra Credit : Wenn ich Probleme wie "Ich frage mich, wie die Kopien gemacht werden" untersuche, setze ich print-Anweisungen in die interessanten Klassenmethoden, so: %Vor%     
Robᵩ 05.04.2012, 20:30
quelle
3

Wie Rob sagte, haben Sie nicht alle drei Konstruktor / Zuweisungsoperatoren erstellt, die C ++ verwendet. Die von ihm erwähnte Dreiregel bedeutet, dass Sie, wenn Sie einen Destruktor, einen Kopierkonstruktor oder einen Zuweisungsoperator ( operator=() ) deklarieren, alle drei verwenden müssen.

Wenn Sie diese Funktionen nicht erstellen, erstellt der Compiler eine eigene Version für Sie. Die Compiler kopieren Konstruktor und Zuweisungsoperatoren jedoch nur eine flache Kopie von Elementen aus dem ursprünglichen Objekt. Das bedeutet, dass das kopierte Objekt, das als Rückgabewert erstellt und dann in das Objekt in main() kopiert wurde, einen Zeiger auf dieselbe Adresse hat wie das erste Objekt, das Sie erstellt haben. Wenn das ursprüngliche Objekt zerstört wird, um Platz für das kopierte Objekt zu schaffen, wird das classSpace-Array auf dem Heap freigegeben, wodurch der Zeiger des kopierten Objekts ungültig wird.

    
Tyler Gill 05.04.2012 20:39
quelle
3

Wenn Sie (endlich) zu dem kommen, was Sie ursprünglich fragen wollten, ist die kurze Antwort, dass es selten ein Problem ist. Der Standard enthält eine Klausel, die explizit ausschließt, dass ein Compiler den Kopierkonstruktor tatsächlich für einen Rückgabewert verwenden muss, selbst wenn der Kopierkonstruktor Nebenwirkungen hat, so dass der Unterschied von außen sichtbar ist.

Je nachdem, ob Sie eine Variable oder nur einen Wert zurückgeben, wird dies entweder als Rückgabewertoptimierung (NRVO) oder nur als Rückgabewertoptimierung (RVO) bezeichnet. Die meisten vernünftig modernen Compiler implementieren beide (einige, wie g ++ tun es sogar, wenn Sie die Optimierung ausschalten).

Um zu vermeiden, dass der Rückgabewert kopiert wird, übergibt der Compiler die Adresse, an der die Kopie als verdeckter Parameter an die Funktion übergeben wird. Die Funktion konstruiert dann ihren Rückgabewert an dieser Stelle, so dass der Wert nach dem Zurückgeben der Funktion bereits vorhanden ist, ohne kopiert zu werden.

Dies ist häufig genug und funktioniert gut genug, dass Dave Abrahams (ein Mitglied des C ++ - Standardkomitees) einen article vor ein paar Jahren, dass mit modernen Compilern die Versuche der Leute, das" Extra-Kopieren "zu vermeiden, tatsächlich Code produzieren, der langsamer ist, als wenn man nur einfachen, offensichtlichen Code schreibt.

    
Jerry Coffin 05.04.2012 20:52
quelle
2

Wenn Sie sehen möchten, wenn eine Kopie eines Objekts erstellt wird, tun Sie dies einfach:

%Vor%     
bames53 05.04.2012 20:51
quelle

Tags und Links