我运行此代码来试验复制构造函数和赋值运算符
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;
没有显示任何消息
的为什么吗
不应该调用复制构造函数或运算符吗?
这是输出:
before AClass b = g() constructor AClass(int) 8 after before AClass c(g()) constructor AClass(int) 8 after
答案 0 :(得分:6)
允许编译器在这种情况下忽略复制。这称为Return Value Optimization。
答案 1 :(得分:4)
在C ++中,允许编译器在几乎所有情况下都删除对复制构造函数的调用,即使复制构造函数具有副作用,例如打印出消息。作为推论,它也允许在几乎任何它想要的点插入对复制构造函数的调用。这使得编写程序来测试您对复制和赋值的理解有点困难,但这意味着编译器可以积极地删除现实代码中不必要的复制。
答案 2 :(得分:2)
这被称为&#34;返回值优化&#34;。如果按值返回对象,则允许编译器在函数返回后在调用者可用的位置构造它;在这种情况下,不会调用复制构造函数。
还允许将其视为普通自动变量,并在返回时复制它,因此复制构造函数必须可用。无论它是否被调用取决于编译器和优化设置,因此您不应该依赖于这两种行为。
答案 3 :(得分: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实例。
答案 4 :(得分:1)
编译器可能已经优化了复制构造函数调用。基本上,它移动对象。
答案 5 :(得分:1)
如果您想查看编译器将调用的构造函数,则必须击败RVO。请更换您的g()
功能:
int i;
AClass g () {
if(i) {
AClass x(8);
return x;
} else {
AClass x(9);
return x;
}
}