2017-11-23 1 views
3

Hier ist ein Beispiel für Friend-Funktionen im Internet gefunden:Local varible (Stack) zurückgegeben als Funktion Rückgabewert

#include <iostream> 
using namespace std; 

class Rectangle { 
    int width, height; 
    public: 
    Rectangle() {} 
    Rectangle(const Rectangle &r) { 
     width = r.width; 
     height = r.height; 
     cout << "copy\n"; 
    } 
    Rectangle (int x, int y) : width(x), height(y) {} 
    int area() {return width * height;} 
    friend Rectangle duplicate (const Rectangle&); 
}; 

Rectangle duplicate (const Rectangle& param) 
{ 
    Rectangle res; 
    res.width = param.width*2; 
    res.height = param.height*2; 
    return res; 
} 

int main() { 
    Rectangle foo; 
    Rectangle bar (2,3); 
    foo = duplicate (bar); 
    cout << foo.area() << '\n'; 
    return 0; 
} 

Ausgang:

24 

Beachten Sie, dass der Freund "Duplikat" Funktion erstellt eine lokale Variable und gibt als Rückgabewert an den Aufrufer zurück. Soll das nicht eine lokale Variable sein und wird auf diesem Stack vergeben? Sollte es nicht zerstört werden, sobald "duplicate" die Ausführung beendet hat? Ist dieses Beispiel gut?

Danke.

+2

Rückgabe der Variablen erstellt eine Kopie davon. – Barmar

+5

Konzeptuell wird die lokale Variable 'res' auf dem Stack erstellt und dann in den Rückgabewert verschoben. Anschließend wird der Destruktor der lokalen Variablen ausgeführt.In der Praxis optimiert der Compiler diese Verschiebung – Justin

+1

Der einzige Fall, in dem dies nicht funktioniert, ist, wenn Sie versuchen, ein Array zurückzugeben, weil Arrays zu Zeigern zerfallen, wenn Sie sie zurückgeben, und der Zeiger auf ein zerstörtes Objekt zeigt. – Barmar

Antwort

10

Man denke nur an regelmäßigen Typen:

int getInt(void) { 
    int a = 5: 
    return a; 
} 

Die Funktion kehrt nicht wirklich den lokalen Variable a. Stattdessen gibt es eine Kopie von a zurück. In ähnlicher Weise wird res von der Funktion nicht zurückgegeben, sondern ihre Kopie.

In der Praxis erkennt der Compiler wahrscheinlich Ihre Funktion und optimiert die Funktion, indem das Kopieren Rectangle vermieden wird.

die Konstruktoraufrufe beobachten, kompiliert es mit

g++ -fno-elide-constructors foo.cpp -o foo 

Sie müssen disable return value optimization for g++ (-fno-elide-constructors), die eine sehr einfache Optimierung ist, dass auch bei -O0 eingeschaltet würde.

+0

Sollte das den Kopierkonstruktor nicht aufrufen? Ich habe den Code geändert, um einen Kopierkonstruktor hinzuzufügen (siehe den aktualisierten Code oben), aber der neue Konstruktor wurde nicht aufgerufen. – hebbo

+0

Wie kann ich es deaktivieren? Ich würde gerne überprüfen können, ob ich diese Voodoo-Magie verstehe. – hebbo

+0

habe ich gerade getan. Immer noch keine Änderung der Ausgabe. Bedeutung Kopierkonstruktor wurde nicht aufgerufen. Ich verwende MinGW auf Windows BTW. Ich werde später g ++ zur Überprüfung verwenden. – hebbo

3

Ihre Funktion gibt Rechteck nach Wert zurück, also sollte es in Ordnung sein.

Das zurückgegebene Objekt befindet sich nicht im selben Speicher. Es ist nicht das gleiche Objekt, das von der lokalen Variablen vom abstrakten Sprachlevel deklariert wird. De-facto, wie Justin sagt, kann der Compiler die Erzeugung temporaler Objekte optimieren.

würden Sie in rot, wenn man

Rectangle& duplicate (const Rectangle& param) 
erklärt

dann würde Funktion versuchen, einen Verweis auf lokale Variable zurückzukehren.

Wie Code geschrieben wird, ist es ein Äquivalent von Ausdruck.

return Rectangle(param.width*2, param.height*2); 

Copykonstruktor:

Rectangle(const Rectangle &r) { 
    width = r.width; 
    height = r.height; 
} 

sollte proprly als

geschrieben werden
Rectangle(const Rectangle &r): width(r.width), height(r.height) 
{ 
} 

Die ouput Linie ist ein Nebeneffekt, dass durch Kopie elision weggelassen werden könnten.

+0

Sollte das den Kopierkonstruktor nicht aufrufen? Ich habe den Code geändert, um einen Kopierkonstruktor hinzuzufügen (siehe den aktualisierten Code oben), aber der neue Konstruktor wurde nicht aufgerufen. – hebbo

+0

@hebbo RVO (Rückgabewert-Optimierung) und Kopie Elision. Formal kann der Compiler den Kopierkonstruktor weglassen, deshalb sollten Sie sich nicht auf Seiteneffekte im Körper verlassen. Ich flehe Sie an, den ISO-Entwurf zu durchsuchen und danach zu suchen. oder suche SO nach diesen Schlüsselwörtern, um Beispiele zu erhalten. – Swift

+0

Wo werden diese Werte gespeichert? Wie überleben sie das Ende des Funktionsaufrufs? Was bestimmt ihren Umfang? – hebbo