当一个函数有一个通过值传递的对象时,它使用复制构造函数或按位复制来创建临时放置在堆栈上以在函数内部使用,如何从函数返回一些对象?
//just a sample code to support the qn
rnObj somefunction()
{
return rnObj();
}
Run Code Online (Sandbox Code Playgroud)
并解释了如何将返回值带到被调用函数.
可以通过其他答案来判断 - 编译器可以对此进行优化.
使用MSVC生成的具体示例,用于解释这是如何实现的(如其中一条评论中所述) -
上课 -
class AClass
{
public:
AClass( int Data1, int Data2, int Data3 );
int GetData1();
private:
int Data1;
int Data2;
int Data3;
};
Run Code Online (Sandbox Code Playgroud)
随着以下琐碎的实施 -
AClass::AClass( int Data1, int Data2, int Data3 )
{
this->Data1 = Data1;
this->Data2 = Data2;
this->Data3 = Data3;
}
int AClass::GetData1()
{
return Data1;
}
Run Code Online (Sandbox Code Playgroud)
以下调用代码 -
AClass Func( int Data1, int Data2, int Data3 )
{
return AClass( Data1, Data2, Data3 );
}
int main()
{
AClass TheClass = Func( 10, 20, 30 );
printf( "%d", TheClass.GetData1() );
}
Run Code Online (Sandbox Code Playgroud)
(printf()添加只是为了确保编译器不会优化所有内容......).
在非优化代码中,我们希望Func()在其堆栈上创建一个本地AClass,在那里构造它并将其复制为其返回变量.
但是,生成的程序集实际上看起来像(删除不需要的行) -
_TEXT SEGMENT
___$ReturnUdt$ = 8 ; size = 4
_Data1$ = 12 ; size = 4
_Data2$ = 16 ; size = 4
_Data3$ = 20 ; size = 4
mov eax, DWORD PTR _Data3$[esp-4]
mov ecx, DWORD PTR _Data2$[esp-4]
mov edx, DWORD PTR _Data1$[esp-4]
push esi
mov esi, DWORD PTR ___$ReturnUdt$[esp]
push eax
push ecx
push edx
mov ecx, esi
call ??0AClass@@QAE@HHH@Z ; AClass::AClass
mov eax, esi
pop esi
ret 0
Run Code Online (Sandbox Code Playgroud)
3个函数变量从堆栈中提取并放入eax,ecx和edx中.
另外的第四个值被放入esi(并传递给ecx).
使用堆栈上的3个参数调用构造函数,并且ecx仍然包含第四个值.
我们来看看构造函数 -
_TEXT SEGMENT
_Data1$ = 8 ; size = 4
_Data2$ = 12 ; size = 4
_Data3$ = 16 ; size = 4
mov edx, DWORD PTR _Data2$[esp-4]
mov eax, ecx
mov ecx, DWORD PTR _Data1$[esp-4]
mov DWORD PTR [eax], ecx
mov ecx, DWORD PTR _Data3$[esp-4]
mov DWORD PTR [eax+4], edx
mov DWORD PTR [eax+8], ecx
ret 12 ; 0000000cH
Run Code Online (Sandbox Code Playgroud)
3个构造函数参数被读入eax的偏移量 - eax是ecx的副本,是上面调用的第四个参数.
因此,构造函数构建了告诉它的对象--Func()的第四个参数.
而且,你猜对了,Func()的第四个参数实际上是整个程序中存在构造的AClass的真正单个位置.让我们看看main()的相关部分 -
_TEXT SEGMENT
_TheClass$ = -12 ; size = 12
_main PROC
sub esp, 12
push 30
push 20
lea eax, DWORD PTR _TheClass$[esp+20]
push 10
push eax
call ?Func@@YA?AVAClass@@HHH@Z ; Func
Run Code Online (Sandbox Code Playgroud)
为AClass保留12个字节,并传递Func()的三个参数以及第四个参数 - 指向这12个字节.
这是特定编译器的特定示例.其他编译器的做法不同.但这就是事物的精神.