C++构造函数、拷贝构造函数、赋值运算符漫谈(二)——函数返回值

 首先我们先看一下C程序的返回值处理情况,我们知道当C函数返回int等小型数据时直接将返回值放入eax寄存器。那当返回大的数据结构又是如何处理呢?看如下一段代码:

#include <stdio.h>
typedef struct big_thing
{
char buf[128];
}big_thing;
big_thing return_test()
{
big_thing b;
b.buf[0]=0;
retutn b;
}
int main(int argc, char *argv[])
{
big_thing n=return_test();
return 0;
}

根据这段代码的汇编代码可以知道这段代码是这样执行的:

1. 首先main函数在栈上开辟了一片空间,并将这块空间的一部分作为传递返回值的临时对象temp;

2. 将temp对象的返回地址作为隐含参数传递给return_test();

3. return_tes()函数将数据拷贝给temp对象(对应下图复制1),并将temp对象的地址用eax寄存器传出;

4. return_test()函数返回后,main函数将eax指向的temp对象拷贝给对象n(对应下图复制2)。

(上图为函数调用的栈情况,绿色部分代表main函数的活动记录,黄色部分为return_test的活动记录。)

  看过C程序的返回值得处理过程,我们不妨先推测一下C++的返回值处理过程:总体与C的返回过程类似,只不过在上图复制1中并不是直接执行“位逐次拷贝”,而是调用拷贝构造函数(因为此时产生了新对象——temp),在复制2的时候需要调用赋值运算符(因为此时没有新的对象产生)。下面我们进行验证。

class X
{
public:
X()
{cout<<"X()"<<endl;};
X(const X& x)
{
cout<<"X(const X& x)"<<endl;
}
X& operator=(const X&)
{
cout<<"="<<endl;
return *this;
}
~X()
{cout<<"destructor"<<endl;}
};

X return_test()
{
   X x;
   cout<<"before return"<<endl;
   return x;
}
int _tmain(int argc, _TCHAR* argv[])
{
X xx;
xx=return_test();
}

运行结果如下:

 

     我们看到结果和我们预想的一样,在return前调用了拷贝构造函数(用来生成临时对象),函数返回后,函数内部的临时变量x先销毁,然后调用赋值运算符(对应图中复制2),将临时对象拷贝给xx;之后销毁临时对象temp,main退出时销毁xx。

 

2.1 RVO

那么如果我们将main中的两句简化为一句:

xx=return_test();

结果又如何呢?运行之后结果如下:

 

     我们发现不再有赋值运算符被调用,也就是“复制2”不在发生,但是拷贝构造函数依然调用,说明“复制1”还是发生了。这是什么原因呢?

     首先,我们必须清楚C++创建对象的方式,不要以为创建对象都要调用无参构造函数,当有“=”时是需要调用拷贝构造函数的(这句话也不准确,祥见系列三)。那么具体是如何发生的呢?这里我们介绍一下C++编译器采用的一个优化Return Value Optimization(RVO)。

RVO:

     RVO其实就是编译器直接将返回对象通过拷贝构造函数构建在目的对象位置上,而不是临时对象(这种情况不再有临时对象);

 具体到编译器是这样执行的:

1. 首先为return_test加上一个额外参数(隐含参数);类型是Class Object的一个引用(同C程序中的临时变量的目的地址,不过此处不再是临时变量,而是目的对象的地址)。这个参数用来放置被“拷贝构建”而返回的值。

2. return前安插了一个拷贝构造函数的调用操作,一边将返回的对象的内容(return_test中的局部变量x)当做上述新增参数的初值。

 也就是说return_test将会被改变为:

void return_test(X& result)

{

X x;

result.X::X(x);    //编译器产生的操作

    return;

}

而对 xx=return_test();的调用也会被修改为

  X xx;//(注:这里不再调用构造函数,只分配空间)

 return_test(xx);

2.2 扩展

如果return_test().memfun()又该怎么被修改调用呢(其中memfun是X的成员函数)?

答案是:X temp;(注:此处仍不会调用构造函数,只是开辟空间,内容由随后的拷贝构造函数构造)

        (return_test(temp),temp).memfun();(注意逗号表达式) 

总结:无论采用什么方式,C++返回对象时总是伴随至少一次复制,所以应尽量避免返回对象。

时间: 2024-07-28 23:19:28

C++构造函数、拷贝构造函数、赋值运算符漫谈(二)——函数返回值的相关文章

调用函数-怎样将二维数组作为函数返回值并在主函数中调用

问题描述 怎样将二维数组作为函数返回值并在主函数中调用 我的调用函数是这样的:float(*TIME(float f_1[3][100]))[100]{ for(i=0;i<=2;i++) { L_1[i][0]=-(a_1*f_1[i][0-3+100]+a_2*f_1[i][0-2+100]+a_3*f_1[i][0-1+100]+a_4*f_1[i][0]+a_5*f_1[i][0+1]+a_6*f_1[i][0+2]); L_1[i][1]=-(a_1*f_1[i][1-3+100]+a

函数返回值是否使用引用类型的问题:理解引用、返回值

在<对象更有用的玻璃罩--常引用>一文中,介绍了对象作为函数的参数时,推荐使用引用的形式.并且,如果实际参数的值不允许改变时,声明为常引用更佳. 在<第8周-任务1-方案3-复数类中运算符重载(与实数运算)>中,又讨论了一个问题,结论是:在类似复数加法运算符重载这样的函数,形式参数用作为常引用最佳,如: friend Complex operator + (const Complex &c, const double &d); friend Complex oper

c++-在写函数时 是不是要把返回值都放在形参表里?把函数返回值设定为true/false?

问题描述 在写函数时 是不是要把返回值都放在形参表里?把函数返回值设定为true/false? 在写函数时 是不是要把返回值都放在形参表里?把函数返回值设定为true/false? 解决方案 主要还是看程序需要把,这种想法只是对于一些内存申请操作来说,函数在结束后就将函数内申请的控件释放.如果已经在外部申请好了内存,是可以当做返回值返回的.所以说并不一定就要把返回值放在形参表里. 解决方案二: 这也是一种方式,尤其对于要返回多个结果的时候,一般通过参数来传递结果.然后函数返回值来表示函数是否调用

函数返回值的检查方法----历史遗留问题?

问题描述 网上众多的源码,在创建事件或者创建线程之后,会将其句柄与INVALID_HANDLE_VALUE进行比较,检查函数的执行是否成功,如下所示:m_hExitEvt=CreateEvent(NULL,FALSE,FALSE,NULL);if(m_hExitEvt==INVALID_HANDLE_VALUE){returnFALSE;}问题出现了:在d:ProgramFilesMicrosoftVisualStudio.NET2003vc7PlatformSDKincludepdh.h和d:

plsql调用函数返回值为空

问题描述 plsql调用函数返回值为空 函数: create or replace function avg_pric(v_ctgry in varchar2,v_pric in out number) return number is Result number; v_qnty number; begin if v_pric is null then v_pric := 0; end if; select avg(productprice),min(quantity) into v_pric,

关于c++函数返回值为const引用的问题.

问题描述 关于c++函数返回值为const引用的问题. 假设 函数 const int& A(); 那么 int b = A(); 和 const int& c = A(); 我理解的是b应该是一个拷贝,c是一个不能被改变的引用,是这样么? 解决方案 对,b是拷贝,c是临时对象引用,生命期延长到当前空间

c++函数返回值是数组问题

问题描述 c++函数返回值是数组问题 比如现在要求定义一个函数fun(),在主函数调用时直接用fun():就能输出在fun函数里定义好的一个字符串,那要fun函数的返回值怎么返回才行,为什么我返回指针但打印出来却不是正确结果?求大神解释 解决方案 一般来说,出于内存管理的需要,让主程序来分配内存,传指针.比如scanf函数,就是通过这个方式输入值. 比如 void getstr(char * buffer) { char str[] = "hello world"; strcpy(bu

函数调用-c语言中函数返回值类型的问题

问题描述 c语言中函数返回值类型的问题 看一道改错题: #include #include #include proc(int k)//很显然根据主调函数和返回值类型知道这里缺少类型 { int n;float s,w,p,q; n=1; s=1.0; while(n<=k){ w=2.0*n; p=w-1.0; q=w+1.0; s=s*w*w/p/q; n++; } return s; } void main(){ system("CLS"); printf("%f

前端-【一个神奇的问题】js函数返回值的问题

问题描述 [一个神奇的问题]js函数返回值的问题 两个js函数: function a(){ var str = '你好~'; return str; } 跪求解答: 我如何得到a()函数返回的值保存到一个变量里面去? 像java里这样 var b =a();我试过了,得不到值... 拜谢哇 解决方案 看看你是不是有定义了别的叫a的函数或者变量.光这么写没有问题. 解决方案二: function aaa(){ var str='你好~'; function bbb(){ alert(str);

android 或java函数返回值,应该如何写才对

问题描述 android 或java函数返回值,应该如何写才对 下面是我遇到的问题: 解决方案 你这样肯定不行:你在你的这个方法里面注册来一个检测器来获取你需要的string,其中的onListDepartment()方法应该只会在你的getDepartmentName 方法只会才会调用,所以你的strDepartmentNameText应该时没有更新的.所以,想要得到strDepartmentNameText方法很多呀,比如在onListDepartment()方法直接使用,或者使用handl