1
#include <iostream>
using namespace std;

class X {
        public:
                X() {
                        cout<<"Cons"<<endl;
                }
                X(const X& x){
                        cout<<"Copy"<<endl;
                }
                void operator=(const X& x){
                        cout<<"Assignment called";
                }
};

X& fun() {
        X s;
        return s;
}

int main(){
        X s = fun();
        return 0;
}

此代码还调用复制构造函数。为什么这行得通?我记得我第一次运行这个程序时,它出现了段错误。但过了一会儿,它开始称这个副本为缺点。现在工作!奇怪。

但是如果我替换, fun() 如下:

X fun() {
        X s;
        return s;
}

然后复制缺点。不叫。我认为副本的缺点。在这种情况下会被调用。但正如@flyfishr64 所指出的,RVO 正在发挥作用。但它仍然没有解释我返回参考的情况。我认为它应该总是段错误。

有什么解释吗?

4

4 回答 4

4

这将返回对堆栈上的对象的引用,该对象在方法返回后不存在 - 堆栈已展开,内存仍然存在,但您不应该使用它

X& fun() {
        X s;
        return s;
}

当您将其更改为:

X fun() {
        X s;
        return s;
}

您现在要返回一份副本。如果编译器足够聪明,它可能会:

X fun() {
    return X();
}

在这种情况下,X直接在调用者堆栈中分配,因此不需要复制。

是否存在段错误取决于您是否正在访问无效内存。

在您的示例中,您不会访问结构中的任何值。要查看段错误,首先保留您返回的引用,fun()并将一些变量添加到结构X中,然后在调用另一个方法返回后,fun()该方法在堆栈上内部分配一些内存(这应该覆盖Xin使用的原始内存fun)并将一些值存储在堆栈上(最好0)。在第二种方法返回后,尝试使用从...X返回的原始引用打印出值fun

于 2009-11-27T04:35:13.580 回答
2

在这段代码中:

X fun() {
        X s;
        return s;
}

复制构造函数没有被调用,因为返回值优化允许编译器绕过创建局部变量's'并直接在返回的变量中构造 X。

您可以在此处阅读有关 RVO的更多信息

于 2009-11-27T04:31:09.370 回答
2

扩展@flyfishr64 的答案

这里调用了复制构造函数,因为:

X s = fun();

是一个初始化。您正在使用 fun() 构造对象,而不是调用默认构造函数。它相当于:

X s(fun());

您看到打印出来的“缺点”是针对 fun() 中的实例的。有关更多信息,请参阅这篇文章:C++中的赋值运算符。

于 2009-11-27T04:35:08.707 回答
0

当您返回对这样的局部变量的引用时,您正在调用未定义的行为。

它恰好在这种情况下起作用,因为class X实际上没有任何函数使用this指针,所以它不再有效并不重要。

于 2010-01-28T13:52:27.757 回答