函数返回值与临时变量的问题

时间:2022-06-08 02:22:00
 

class X
{
int i;
public:
X(int ii=0);
X(X &a);
~X();
void modify();
};


X::X(int ii)
{
i=ii;
cout<<"constructor   "<<this->i<<endl;
}


X::X(X &a)
{
i=a.i;
cout<<"copy   "<<this->i<<endl;
}


X::~X()
{
cout<<"destructor   "<<this->i<<endl;
}


void X::modify()
{
i++;
}


X f5()
{
return X();
}


void main()
{
X  a(10);
f5()=a;

}


/////////////////////////////
结果是:
constructor   10     //对象a构造函数
constructor   0      //f5()函数中X()构造函数
destructor    10     //析构
destructor    10     //析构

编译器不是要产生一个临时对象来保存f5()返回值吗??
可是从结果上看并没有调用构造函数或拷贝构造函数来产生一个临时变量来接受对象a的赋值啊????

16 个解决方案

#1



X f5() 

return X(); 
/*
对这个语句,编译器明白对创建的对象没有其他需求,只是返回它,所以编译器直接
    把这个对象创建在外部返回值的内存能单元,并没有去创建一个局部变量来返回.
*/

#2


to  ckt1120:
这个我明白,这是返回值优化。

我有疑问的是求表达式f5()=a期间,编译器不是应该创建一个临时对象保存f5()返回值的值,并接受a的赋值吗??? 

#3


编译器不是应该创建一个临时对象保存f5()返回值的值,并接受a的赋值吗
-----------
是的

你调用了一次默认构造函数,constructor       0    
就是去初始化f5返回值的

#4


我来回答下lz的疑问
我记得在thinking in C++里面有句话
大意是
求表达式的期间,要创建临时变量,进行构造和删除。不过这些,我们往往是 看不见
是编译器的事情

#5


constructor      0  是f5()函数体内X()产生的吧???
    编译器明白对创建的对象没有其他需求,只是返回它,所以编译器直接
    把这个对象创建在外部返回值的内存能单元。

之后发现要用到函数返回值在一个表达式中,编译器不是应该再构造一个临时变量来接受a的赋值吗???
一个是函数返回值,一个是临时变量

#6


to andy_cai:
哈,是啊,我们看不见,但是通过构造函数,拷贝构造函数可以看到临时变量的产生啊
就像值传递的时候,形参是实参的副本,这副本就是一个临时变量,它保存在栈中。我们可以在构造函数中加入输出语句,看到这个临时变量构造出来了。

#7


你还没搞明白?

这里没有产生临时变量,直接用X()去初始化返回值的。

实际上,对于有返回值的函数调用,编译器会把返回值的地址压栈,
然后在return语句之前,初始化返回值

f5函数可能的伪码:
void f5(X& _rlt) // 返回值地址
{  
  _rlt.X::X(10, 11); // 初始化返回值
  return;


具体的你可以查阅深度探索C++对象模型

#8


就这么和你说吧,你先要弄清楚拷贝构造函数和重载运算符=,在F5里这个临时对象是已经通过构造函数初始化了的,所以
F5()=a ;不会调你的拷贝函数,而重载运算符=是你的左值初始化后调用的!你可以再写个重载运算符=就知道了!!!

#9


LS正解,MAIN中局部变量被当做引用转递参数传进f5,
而且编译器肯定经过了Name Returned Value 优化,
不然应该是三次构造,三次析构

#10


楼上说的我都明白。

关键是对于表达式f5()=a,是把a的内容直接赋值给函数f5()的返回值那块内存,还是函数f5()从已经生成的返回值再产生一个临时变量去接受a的值啊???

#11


learning...

#12


楼主可以试试:
将X f5()函数该为X& f5()后输出结果是:
constructor       10          
constructor       0             
destructor        0           //注意这里 
destructor        10
先顶一个,我自己先想想   
        

#13



X f5() 

    return X(); 


这个函数里面构造了一个X类的对象;但是当函数结束的时候对象并没有随之析构,而是再main结束的时候析构调的,显然这个对象具有全局性;
至于f5() = a;纯粹就是将一个对象付给另一个对象,系统提供了默认的operator=支持,当然了这里不会调用任何的函数,也没有什么临时对象;

#14


那么将该函数写成返回一个引用,函数里面创建的对象在该函数结束时就撤销了,也就是纯粹的临时变量?
这之间区别在哪?编译器怎么对待两者?

#15


f5()=a; 
上面这句话还是没有理解

#16


将f5()里X()改为X p;return p;就可以得到你想要的结果

#1



X f5() 

return X(); 
/*
对这个语句,编译器明白对创建的对象没有其他需求,只是返回它,所以编译器直接
    把这个对象创建在外部返回值的内存能单元,并没有去创建一个局部变量来返回.
*/

#2


to  ckt1120:
这个我明白,这是返回值优化。

我有疑问的是求表达式f5()=a期间,编译器不是应该创建一个临时对象保存f5()返回值的值,并接受a的赋值吗??? 

#3


编译器不是应该创建一个临时对象保存f5()返回值的值,并接受a的赋值吗
-----------
是的

你调用了一次默认构造函数,constructor       0    
就是去初始化f5返回值的

#4


我来回答下lz的疑问
我记得在thinking in C++里面有句话
大意是
求表达式的期间,要创建临时变量,进行构造和删除。不过这些,我们往往是 看不见
是编译器的事情

#5


constructor      0  是f5()函数体内X()产生的吧???
    编译器明白对创建的对象没有其他需求,只是返回它,所以编译器直接
    把这个对象创建在外部返回值的内存能单元。

之后发现要用到函数返回值在一个表达式中,编译器不是应该再构造一个临时变量来接受a的赋值吗???
一个是函数返回值,一个是临时变量

#6


to andy_cai:
哈,是啊,我们看不见,但是通过构造函数,拷贝构造函数可以看到临时变量的产生啊
就像值传递的时候,形参是实参的副本,这副本就是一个临时变量,它保存在栈中。我们可以在构造函数中加入输出语句,看到这个临时变量构造出来了。

#7


你还没搞明白?

这里没有产生临时变量,直接用X()去初始化返回值的。

实际上,对于有返回值的函数调用,编译器会把返回值的地址压栈,
然后在return语句之前,初始化返回值

f5函数可能的伪码:
void f5(X& _rlt) // 返回值地址
{  
  _rlt.X::X(10, 11); // 初始化返回值
  return;


具体的你可以查阅深度探索C++对象模型

#8


就这么和你说吧,你先要弄清楚拷贝构造函数和重载运算符=,在F5里这个临时对象是已经通过构造函数初始化了的,所以
F5()=a ;不会调你的拷贝函数,而重载运算符=是你的左值初始化后调用的!你可以再写个重载运算符=就知道了!!!

#9


LS正解,MAIN中局部变量被当做引用转递参数传进f5,
而且编译器肯定经过了Name Returned Value 优化,
不然应该是三次构造,三次析构

#10


楼上说的我都明白。

关键是对于表达式f5()=a,是把a的内容直接赋值给函数f5()的返回值那块内存,还是函数f5()从已经生成的返回值再产生一个临时变量去接受a的值啊???

#11


learning...

#12


楼主可以试试:
将X f5()函数该为X& f5()后输出结果是:
constructor       10          
constructor       0             
destructor        0           //注意这里 
destructor        10
先顶一个,我自己先想想   
        

#13



X f5() 

    return X(); 


这个函数里面构造了一个X类的对象;但是当函数结束的时候对象并没有随之析构,而是再main结束的时候析构调的,显然这个对象具有全局性;
至于f5() = a;纯粹就是将一个对象付给另一个对象,系统提供了默认的operator=支持,当然了这里不会调用任何的函数,也没有什么临时对象;

#14


那么将该函数写成返回一个引用,函数里面创建的对象在该函数结束时就撤销了,也就是纯粹的临时变量?
这之间区别在哪?编译器怎么对待两者?

#15


f5()=a; 
上面这句话还是没有理解

#16


将f5()里X()改为X p;return p;就可以得到你想要的结果