从函数返回对象时是否有临时创建?

时间:2009-05-16 07:21:49

标签: c++ function

当一个函数有一个通过值传递的对象时,它使用复制构造函数或按位复制来创建临时放置在堆栈上以在函数内部使用,如何从函数返回一些对象?

//just a sample code to support the qn
rnObj somefunction()
{
return rnObj();
}

并解释了如何将返回值带到被调用函数。

4 个答案:

答案 0 :(得分:6)

可以通过其他答案来判断 - 编译器可以对此进行优化。

使用MSVC生成的具体示例,用于解释如何这是可能的(如其中一条评论中所述) -

上课 -

class AClass
{
public:
   AClass( int Data1, int Data2, int Data3 );

   int GetData1();

private:
   int Data1;
   int Data2;
   int Data3;
};

通过以下简单的实施 -

AClass::AClass( int Data1, int Data2, int Data3 )
{
    this->Data1 = Data1;
    this->Data2 = Data2;
    this->Data3 = Data3;
}

int AClass::GetData1()
{
    return Data1;
}

以下调用代码 -

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() );
}

(添加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

从堆栈中提取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

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

为AClass保留12个字节,并传递Func()的三个参数以及第四个参数 - 指向这12个字节。

这是特定编译器的具体示例。其他编译器的做法不同。但这就是事物的精神。

答案 1 :(得分:3)

C ++标准规定编译器可以选择使用复制构造函数创建临时对象,也可以选择优化临时对象。

有一篇关于这个主题的维基百科文章,有很多参考文献here

答案 2 :(得分:1)

允许编译器在返回时执行复制。大多数编译器都不会。但是,您必须确保rnObj具有可访问的复制构造函数。

答案 3 :(得分:1)

如果打开优化,大多数现代编译器都可以避免临时对象创建。有关详细信息,请参阅Named Return Value Optimization