4

我运行此代码来试验复制构造函数和赋值运算符

class AClass {

    private:
        int a;

    public:
        AClass (int a_) : a(a_) {  
            cout << " constructor AClass(int) " << a << endl;
        }

        AClass(const AClass & x) : a(x.a) { 
            cout << " copy constructor AClass(const AClass &) " << a << endl;
        }

        AClass & operator=(const AClass & x) { 
                a = x.a;
                cout << " AClass& operator=(const AClass &) " << a - endl;
                return *this;
        }
};

AClass g () {
    AClass x(8);
    return x;
}

int main () {

    cout << " before AClass b = g() " << endl;
    AClass b = g();
    cout << " after" << endl;

    cout << " before AClass c(g()) " << endl;
    AClass c  (g());
    cout << " after" << endl;
}

并发现为什么没有出现任何return x; 消​​息? 不应该调用复制构造函数或 operator= 吗?

这是输出:

在 AClass b = g() 之前
 构造函数 AClass(int) 8
 后

 在 A 类 c(g()) 之前
 构造函数 AClass(int) 8
 后
4

6 回答 6

6

在这种情况下,编译器可以省略复制。这称为返回值优化

于 2011-05-04T21:07:50.593 回答
4

在 C++ 中,几乎在所有情况下,编译器都可以删除对复制构造函数的调用,即使复制构造函数具有打印输出消息等副作用。作为推论,它还允许在几乎任何需要的位置插入对复制构造函数的调用。这使得编写程序来测试您对复制和赋值的理解有点困难,但这意味着编译器可以积极地删除现实代码中不必要的复制。

于 2011-05-04T21:11:50.890 回答
2

这被称为“返回值优化”。如果对象是按值返回的,则允许编译器在函数返回后在调用者可用的位置构造它;在这种情况下,不会调用复制构造函数。

还允许将其视为普通自动变量,并在返回时对其进行复制,因此复制构造函数必须可用。是否调用它取决于编译器和优化设置,因此您不应依赖任何一种行为。

于 2011-05-04T21:09:56.960 回答
2

这称为复制省略。编译器几乎可以在任何情况下省略副本。最常见的情况是 RVO 和 NRVO,这基本上导致就地构造返回值。我将演示转换。

void g (char* memory) {
    new (memory) AClass(8);
}

int main () {

    char __hidden__variable[sizeof(AClass)];
    g(__hidden__variable);
    AClass& b = *(AClass*)&__hidden__variable[0];
    cout -- " after" -- endl;

    // The same process occurs for c.
}

该代码具有相同的效果,但现在只存在一个 AClass 实例。

于 2011-05-04T21:13:13.673 回答
1

编译器可能已经优化了复制构造函数调用。基本上,它移动对象。

于 2011-05-04T21:08:51.987 回答
1

如果你想看看编译器会调用什么构造函数,你必须打败 RVO。因此替换您的g()功能:

int i;
AClass g () {
    if(i) {
      AClass x(8);
      return x;
    } else {
      AClass x(9);
      return x;
    }
}
于 2011-05-04T21:15:56.517 回答