再问函数返回值产生临时变量的问题

时间:2022-04-27 02:20:34
#include <iostream>
using namespace std;

class A
{
public:
A()
{
cout << "A()\n";
}
A(const A& other)
{
cout << "A(const A& other)\n";
}
~A()
{
cout << "~A()\n";
}
};

A fun()
{
//A a;
//return a;

return A();
}

int main()
{
A b = fun();

}

返回局部变量a的话, 会调用一次构造, 一次拷贝构造, 并没有再生成一个中间临时变量
返回A()应该就是RVO优化了, 只需一个构造函数
我想问的是, 为什么int型变量就会产生一个中间临时变量, 而类对象就没了呢
也是优化吗

23 个解决方案

#1


这是个秘密、、、、一个不为人知的秘密、、、、

#2


int会有一个副本,对于类,会有浅复制和深复制之分,你可以写自己的拷贝方法,去实习深复制

#3


我问的是int有副本, 类对象怎么没副本了
引用 2 楼 bdmh 的回复:
int会有一个副本,对于类,会有浅复制和深复制之分,你可以写自己的拷贝方法,去实习深复制

#4


引用 3 楼 q191201771 的回复:
我问的是int有副本, 类对象怎么没副本了

这个问题没法具体回答你,类里可能有非常复杂的组成,没必要默认就创建副本,浪费不必要的内存,特别市里面的指针类型,副本就要重新开辟空间,完全没有必要,除非你需要,自己写

#5


应该是优化了

#6


同意楼上的,应该是优化了,而int 内置类型没有优化的必要。

#7


引用 3 楼 q191201771 的回复:
我问的是int有副本, 类对象怎么没副本了

引用 2 楼 bdmh 的回复:

int会有一个副本,对于类,会有浅复制和深复制之分,你可以写自己的拷贝方法,去实习深复制

int会有一个副本?何处此言?
如果返回值为int的话,应该就是通过eax寄存器吧?

#8


引用 7 楼 we_sky2008 的回复:
如果返回值为int的话,应该就是通过eax寄存器吧?

是,eax用作返回值

#9


A fun()
{
    return A();
}
会被编译器处理为:
void fun(A *__result)//编译器需要先为返回值分配空间,然后将该空间的地址传递给func, 然后以该地址为this指针构造对象
{
    __result->A::A();//在返回值的地址上执行构造函数
}

对于如下调用:
A a = fun();

//先为a分配空间,然后将该空间的地址传递给fun
void fun(&a)
{
    &a->A::A();//在a的空间上直接调用构造函数将a构造
}

而如下调用:
fun();
则是先分配一个临时的返回值空间,
A tmp;//注意,编译器在这里只是分配空间,并不调用构造函数,
然后
void fun(&tmp)
{
    &tmp->A::A();//在tmp的空间上直接调用构造函数将tmp构造
}
然后,在函数调用结束后tmp又被析构

#10


这个就是返回值优化,分为具名返回值优化和匿名返回值优化,你这个属于后者。

#11


int时的eax是中间变量,寄存器就是来传值的值给等号右边,别和对象情况混在一起,对象是把左边的地址直接传进函数在函数内直接构造左边,ALT+8跟踪一遍就妥了~!

#12


when a temporary class object that has not been bound to a reference (12.2) would be copied to a class
object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary
object directly into the target of the omitted copy

#13


回9楼
你的意思是说
A fun()
{
A a;
return a;
}
当A b = fun()时就将b用a拷贝构造
当fun()时就生成一个tmp对象用a来拷贝构造吗

#14


纠正下!!eax“不”是中间变量
引用 11 楼 ljt3969636 的回复:
int时的eax“不”是中间变量,寄存器就是来传值的值给等号右边,别和对象情况混在一起,对象是把左边的地址直接传进函数在函数内直接构造左边,ALT+8跟踪一遍就妥了~!

#15


in a return statement in a function with a class return type, when the expression is the name of a
non-volatile automatic object with the same cv-unqualified type as the function return type, the copy
operation can be omitted by constructing the automatic object directly into the function’s return value

#16


A fun1()
{
00413620  push        ebp  
00413621  mov         ebp,esp 
00413623  sub         esp,0CCh 
00413629  push        ebx  
0041362A  push        esi  
0041362B  push        edi  
0041362C  lea         edi,[ebp-0CCh] 
00413632  mov         ecx,33h 
00413637  mov         eax,0CCCCCCCCh 
0041363C  rep stos    dword ptr es:[edi] 
0041363E  mov         dword ptr [ebp-0C8h],0 
//A a;
//return a;

return A();
00413648  mov         ecx,dword ptr [ebp+8] 
0041364B  call        A::A (411703h) //在&b上直接构造
00413650  mov         eax,dword ptr [ebp-0C8h] 
00413656  or          eax,1 
00413659  mov         dword ptr [ebp-0C8h],eax 
0041365F  mov         eax,dword ptr [ebp+8] 
}

#17


但是如果有if 条件  return obj1; else return obj2;这样的语句,优化就会被阻止

#18


看过《Effective C++》木有?
看过《C++ 编程思想》木有?

好像都有讲。

#19


多写楼上几位指教
不过我想可能有的人把我问的东西搞错了
我要问的是
A fun()
{
    A a;
    return a;
}

int fun()
{
    int a;
    return a;
}
区别所在

#20


引用 13 楼 q191201771 的回复:
回9楼
你的意思是说
A fun()
{
A a;
return a;
}
当A b = fun()时就将b用a拷贝构造
当fun()时就生成一个tmp对象用a来拷贝构造吗

这种情况下,如果编译器实施具名返回值优化的话,会被处理为如下:
void fun(A *__result)
{
    __result->A::A();//用__result来代替局部对象a,避免了拷贝构造函数的调用
}
如果没有具名返回值优化,会被处理为如下,
void fun(A *__result)
{
    A a;
    __result->A::(a);//这里调用拷贝构造函数
}

《深度探索C++对象模型》这本书上有详细解释,楼主不妨看下,

#21


回20楼
意思是开了具名返回值优化
A fun()
{
A a;
return a;
}
A b = fun();
a已经不调构造函数了吗
只是b调用一次构造函数?

#22


引用 19 楼 q191201771 的回复:
多写楼上几位指教
不过我想可能有的人把我问的东西搞错了
我要问的是
A fun()
{
  A a;
  return a;
}

int fun()
{
  int a;
  return a;
}
区别所在

int fun()
{
  int a;
  return a;
}
最后的返回语句return a;
编译器只需要把a的值mov到eax寄存器中就完成了返回值的返回


A fun()
{
  A a;
  return a;
}
编译器必须在某处先开辟一个空间用于保存返回值对象,然后将该空间的地址传递给fun函数
在函数fun的return语句处安插语句将返回值构造出来

#23


引用 21 楼 q191201771 的回复:
回20楼
意思是开了具名返回值优化
A fun()
{
A a;
return a;
}
A b = fun();
a已经不调构造函数了吗
只是b调用一次构造函数?

恩,是的
DEV C++下面就有这个优化了

#1


这是个秘密、、、、一个不为人知的秘密、、、、

#2


int会有一个副本,对于类,会有浅复制和深复制之分,你可以写自己的拷贝方法,去实习深复制

#3


我问的是int有副本, 类对象怎么没副本了
引用 2 楼 bdmh 的回复:
int会有一个副本,对于类,会有浅复制和深复制之分,你可以写自己的拷贝方法,去实习深复制

#4


引用 3 楼 q191201771 的回复:
我问的是int有副本, 类对象怎么没副本了

这个问题没法具体回答你,类里可能有非常复杂的组成,没必要默认就创建副本,浪费不必要的内存,特别市里面的指针类型,副本就要重新开辟空间,完全没有必要,除非你需要,自己写

#5


应该是优化了

#6


同意楼上的,应该是优化了,而int 内置类型没有优化的必要。

#7


引用 3 楼 q191201771 的回复:
我问的是int有副本, 类对象怎么没副本了

引用 2 楼 bdmh 的回复:

int会有一个副本,对于类,会有浅复制和深复制之分,你可以写自己的拷贝方法,去实习深复制

int会有一个副本?何处此言?
如果返回值为int的话,应该就是通过eax寄存器吧?

#8


引用 7 楼 we_sky2008 的回复:
如果返回值为int的话,应该就是通过eax寄存器吧?

是,eax用作返回值

#9


A fun()
{
    return A();
}
会被编译器处理为:
void fun(A *__result)//编译器需要先为返回值分配空间,然后将该空间的地址传递给func, 然后以该地址为this指针构造对象
{
    __result->A::A();//在返回值的地址上执行构造函数
}

对于如下调用:
A a = fun();

//先为a分配空间,然后将该空间的地址传递给fun
void fun(&a)
{
    &a->A::A();//在a的空间上直接调用构造函数将a构造
}

而如下调用:
fun();
则是先分配一个临时的返回值空间,
A tmp;//注意,编译器在这里只是分配空间,并不调用构造函数,
然后
void fun(&tmp)
{
    &tmp->A::A();//在tmp的空间上直接调用构造函数将tmp构造
}
然后,在函数调用结束后tmp又被析构

#10


这个就是返回值优化,分为具名返回值优化和匿名返回值优化,你这个属于后者。

#11


int时的eax是中间变量,寄存器就是来传值的值给等号右边,别和对象情况混在一起,对象是把左边的地址直接传进函数在函数内直接构造左边,ALT+8跟踪一遍就妥了~!

#12


when a temporary class object that has not been bound to a reference (12.2) would be copied to a class
object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary
object directly into the target of the omitted copy

#13


回9楼
你的意思是说
A fun()
{
A a;
return a;
}
当A b = fun()时就将b用a拷贝构造
当fun()时就生成一个tmp对象用a来拷贝构造吗

#14


纠正下!!eax“不”是中间变量
引用 11 楼 ljt3969636 的回复:
int时的eax“不”是中间变量,寄存器就是来传值的值给等号右边,别和对象情况混在一起,对象是把左边的地址直接传进函数在函数内直接构造左边,ALT+8跟踪一遍就妥了~!

#15


in a return statement in a function with a class return type, when the expression is the name of a
non-volatile automatic object with the same cv-unqualified type as the function return type, the copy
operation can be omitted by constructing the automatic object directly into the function’s return value

#16


A fun1()
{
00413620  push        ebp  
00413621  mov         ebp,esp 
00413623  sub         esp,0CCh 
00413629  push        ebx  
0041362A  push        esi  
0041362B  push        edi  
0041362C  lea         edi,[ebp-0CCh] 
00413632  mov         ecx,33h 
00413637  mov         eax,0CCCCCCCCh 
0041363C  rep stos    dword ptr es:[edi] 
0041363E  mov         dword ptr [ebp-0C8h],0 
//A a;
//return a;

return A();
00413648  mov         ecx,dword ptr [ebp+8] 
0041364B  call        A::A (411703h) //在&b上直接构造
00413650  mov         eax,dword ptr [ebp-0C8h] 
00413656  or          eax,1 
00413659  mov         dword ptr [ebp-0C8h],eax 
0041365F  mov         eax,dword ptr [ebp+8] 
}

#17


但是如果有if 条件  return obj1; else return obj2;这样的语句,优化就会被阻止

#18


看过《Effective C++》木有?
看过《C++ 编程思想》木有?

好像都有讲。

#19


多写楼上几位指教
不过我想可能有的人把我问的东西搞错了
我要问的是
A fun()
{
    A a;
    return a;
}

int fun()
{
    int a;
    return a;
}
区别所在

#20


引用 13 楼 q191201771 的回复:
回9楼
你的意思是说
A fun()
{
A a;
return a;
}
当A b = fun()时就将b用a拷贝构造
当fun()时就生成一个tmp对象用a来拷贝构造吗

这种情况下,如果编译器实施具名返回值优化的话,会被处理为如下:
void fun(A *__result)
{
    __result->A::A();//用__result来代替局部对象a,避免了拷贝构造函数的调用
}
如果没有具名返回值优化,会被处理为如下,
void fun(A *__result)
{
    A a;
    __result->A::(a);//这里调用拷贝构造函数
}

《深度探索C++对象模型》这本书上有详细解释,楼主不妨看下,

#21


回20楼
意思是开了具名返回值优化
A fun()
{
A a;
return a;
}
A b = fun();
a已经不调构造函数了吗
只是b调用一次构造函数?

#22


引用 19 楼 q191201771 的回复:
多写楼上几位指教
不过我想可能有的人把我问的东西搞错了
我要问的是
A fun()
{
  A a;
  return a;
}

int fun()
{
  int a;
  return a;
}
区别所在

int fun()
{
  int a;
  return a;
}
最后的返回语句return a;
编译器只需要把a的值mov到eax寄存器中就完成了返回值的返回


A fun()
{
  A a;
  return a;
}
编译器必须在某处先开辟一个空间用于保存返回值对象,然后将该空间的地址传递给fun函数
在函数fun的return语句处安插语句将返回值构造出来

#23


引用 21 楼 q191201771 的回复:
回20楼
意思是开了具名返回值优化
A fun()
{
A a;
return a;
}
A b = fun();
a已经不调构造函数了吗
只是b调用一次构造函数?

恩,是的
DEV C++下面就有这个优化了