读书人

函数返回一个quot;类对象quot;需要拷贝一份到堆

发布时间: 2012-03-21 13:33:15 作者: rapoo

函数返回一个"类对象",需要拷贝一份到堆栈上面么?
例如,我有下面这个简单的代码:(VC2010)

C/C++ code
struct o{    int x;    int y;};o f(){    int i=0;    int j=1;    o o1={30};    int k=2;    int l=3;    return o1;};int main( void){    o o2=f();     return 0;}

我的问题是: foo函数返回的时候,他的局部变量例如i,j,k,l,s1都已经退栈了不存在了。
那么return s1的返回语句,s1的值还存在么?
如果返回之前需要拷贝一个值到堆栈的顶端,又如何退栈? main里面又如何拿到s1呢?

感觉有点想不通啊。高手解释一下吧!

[解决办法]
return o1会把o1复制到(或者移动到,如果此类型定义了转移构造)一个临时变量里
这种情况如果可能的话最好在return时再创建这个对象 return o(30) 这样编译器会优化 减少一次对象的复制/移动
[解决办法]
补充
o o2=f();
会把刚才说的那个临时对象再复制/移动到o2 这一步通常不能被优化掉
[解决办法]
o o2=f(); // 调用f函数,系统会在运行时栈上开辟一数据结构的内存(活动记录),同时指针指向了该活动记录。该活动记录中存放有函数返回的地址和返回值、局部变量等数据成员。
当函数返回时,系统会pop该活动记录,但这仅是“逻辑”上的删除,物理上数据还在那,在回到Main数时,还是可以访问返回值的。

[解决办法]
转移构造是C++11的新特性 VC10有支持

后面这个优化叫 return value optimization (RVO) 因为你在return时才会创建这个对象 那么这个对象在这个函数体内就不会再被使用了 编译器可以直接用那个临时变量的空间创建这个对象 不需要再额外多创建一份
还有一种更强的优化叫 Named return value optimization (NRVO)是说像你的原始代码那样 函数体内部的命名变量在最后被返回 在特定情况下也可以优化 少一次复制/移动 不过这和编译器的实现相关不一定每次都能优化
[解决办法]
这个返回的对象不在callee的frame里,而在caller的frame
相当于
s s2=s(44, 55);
声明和赋值是同一语句貌似又可以优化成直接构造 相当于
s s2(44, 55);

[解决办法]
Mark一下。
我怎么记得,当返回值是对象,优化时将“左值”(要赋值的对象)当作引用参数传给函数,函数内部实现上是操作的“赋值的对象”,也就是程序中的s2。
将函数改成这样:
s foo(){
printf("foo in\n");
s s1(44,55);
s1.a = 30;
printf("foo returns\n");
return s1;
};

使用release编译,运行后发现只调用了一个ctor,其它的构造函数没有调用
[解决办法]
额 有可能是在调用者的栈内存储的。
[解决办法]
http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx

Optimization Description
Here is a simple example in Figure 1 to illustrate the optimization and how it is implemented:

A MyMethod (B &var)
{
A retVal;
retVal.member = var.value + bar(var);
return retVal;
}
Figure 1. Original code

The program that uses the above function may have a construct such as:

valA = MyMethod(valB);
That value that is returned from MyMethod is created in the memory space pointed to by ValA through the use of hidden argument. Here is what the function looks like when we expose the hidden argument and explicitly show the constructors and destructors:

A MyMethod (A &_hiddenArg, B &var)
{
A retVal;
retVal.A::A(); // constructor for retVal
retVal.member = var.value + bar(var);
_hiddenArg.A::A(retVal); // the copy constructor for A
return;
retVal.A::~A(); // destructor for retVal

}
Figure 2. Hidden argument code without NRVO (pseudo code)

From the above code, it is noticeable that there are some optimization opportunities available. The basic idea is to eliminate the temporary stack-based value (retVal) and use the hidden argument. Consequently, this will eliminate the copy constructor and destructor of the stack-based value. Here is the NRVO-based optimized code:

A MyMethod(A &_hiddenArg, B &var)
{
_hiddenArg.A::A();
_hiddenArg.member = var.value + bar(var);
Return
}
Figure 3. Hidden argument code with NRVO (pseudo code)



读书人网 >C++

热点推荐