【发布时间】:2017-01-28 14:13:48
【问题描述】:
我大致了解函数如何按值返回对象。但我想在较低的层次上理解它。合理的装配水平。
我明白这段代码
ClassA fun(){
ClassA a;
a.set(...);
return a;
}
在内部转换为
void fun(Class& ret){
ClassA a;
a.set(...);
ret.ClassA::ClassA(a);
}
在返回值上有效地调用复制构造函数。
我也知道有一些优化(如 NRVO)可以生成以下代码,避免复制构造函数。
void fun(Class& ret){
ret.set(...);
}
但是我的问题更基本一些。它实际上与特定的对象无关。它甚至可以是原始类型。
假设我们有这个代码:
int fun(){
return 0;
}
int main(){
fun();
}
我的问题是返回对象存储在内存中的什么位置。
如果我们查看堆栈...有main 的堆栈帧,然后是fun 的堆栈帧。返回对象是否存储在某个地址中,例如两个堆栈帧之间?或者它可能存储在main 堆栈帧中的某个位置(并且可能是在生成的代码中通过引用传递的地址)。
我已经考虑过了,第二个似乎更实用但是我不明白编译器如何知道要在main 的堆栈帧中推送多少内存?它是否计算出最大的返回类型并推送它,即使可能会浪费一些内存?还是动态完成,只在调用函数之前分配空间?
【问题讨论】:
-
取决于编译器,但你总是可以look at its assembly。
-
顺便说一句,
ret.ClassA::ClassA(a);无法编译。应该这样写:new (&ret) ClassA(a);. -
强大的实现细节。通常,编译器会尝试在 CPU 寄存器中返回任何内容。如果它不适合,那么它将让调用者在其堆栈帧上保留空间并将指针传递给此存储。在这种情况下可能出现的情况。只需在您自己的编译器上尝试,要求它生成程序集列表。你会得到一个事实而不是猜测。