Ich verstehe im Allgemeinen, wie eine Funktion ein Objekt nach Wert zurückgibt. Aber ich wollte es auf der unteren Ebene verstehen. Montageebene, wenn sinnvoll.Wo wird das Rückgabeobjekt gespeichert?
verstehe ich das dieser Code
ClassA fun(){
ClassA a;
a.set(...);
return a;
}
umgewandelt wird intern auf
void fun(Class& ret){
ClassA a;
a.set(...);
ret.ClassA::ClassA(a);
}
, die den Kopierkonstruktor auf den Rückgabewert effektiv nennen.
Ich verstehe auch, dass es einige Optimierungen (wie NRVO) gibt, die den folgenden Code generieren könnten, der den Kopierkonstruktor vermeidet.
void fun(Class& ret){
ret.set(...);
}
Meine Frage ist jedoch ein bisschen mehr grundlegend. Es hat nicht wirklich mit Objekten im Speziellen zu tun. Es könnten sogar primitive Typen sein.
Können sagen, wir haben diesen Code:
int fun(){
return 0;
}
int main(){
fun();
}
Meine Frage ist, wo ist das Rückgabeobjekt im Speicher abgelegt.
Wenn wir auf den Stapel schauen ... Dort ist der Stapelrahmen von main
und dann der Stapelrahmen von fun
. Ist das Rückgabeobjekt in einer Adresse wie zwischen den beiden Stapelrahmen gespeichert? Oder vielleicht ist es irgendwo im main
Stack-Frame gespeichert (und möglicherweise ist das die Adresse, die als Referenz im generierten Code übergeben wird).
Ich habe darüber nachgedacht und die zweite scheint praktischer, aber ich verstehe nicht, wie der Compiler wissen, wie viel Speicher im Stapelrahmen von main
schieben? Errechnet er, was der größte Rückgabetyp ist und pusht das, obwohl es etwas verschwendete Speicher geben könnte? Oder wird es dynamisch ausgeführt, reserviert es diesen Speicherplatz erst, bevor die Funktion aufgerufen wird?
Hängt vom Compiler ab, aber Sie können immer [siehe Assembly] (https://godbolt.org/g/JztY5G). – chris
Übrigens, 'ret.ClassA :: ClassA (a);' kompiliert nicht. So sollte es geschrieben werden: 'new (& ret) ClassA (a);'. – HolyBlackCat
Starkes Implementierungsdetail. Im Allgemeinen wird der Compiler versuchen, alles in den CPU-Registern zurückzugeben. Wenn es nicht passt, reserviert der Anrufer Speicherplatz auf seinem Stapelrahmen und übergibt einen Zeiger an diesen Speicher. Das wahrscheinlichste Szenario in diesem Fall. Probieren Sie es einfach in Ihrem eigenen Compiler aus, und fordern Sie es auf, einen Assembly-Eintrag zu generieren. Sie haben eine Tatsache statt einer Schätzung. –