据我所知,在以下场景中调用了复制构造函数:
1) Pass by value
2) Return by value
3) When you create and initialize a new object with an existing object
以下是该计划:
#include <iostream>
using namespace std;
class Example
{
public:
Example()
{
cout << "Default constructor called.\n";
}
Example(const Example &ob1)
{
cout << "Copy constructor called.\n";
}
Example& operator=(const Example &ob1)
{
cout << "Assignment operator called.\n";
return *this;
}
~Example()
{
cout<<"\nDtor invoked"<<endl;
}
int aa;
};
Example funct()
{
Example ob2;
ob2.aa=100;
return ob2;
}
int main()
{
Example x;
cout << "Calling funct..\n";
x = funct();
return 0;
}
输出结果为:
默认构造函数。
致电功能..
默认构造函数。
调用分配运算符。
Dtor援引
Dtor援引
请纠正我,IIRC应该发生以下呼叫顺序:
1)x的构造函数称为
2)ob2的构造函数被称为
3)函数返回,因此调用复制构造函数(将ob2复制到未命名的临时变量,即funct())
4)ob2的析构函数叫
5)将未命名的临时变量分配给x
6)销毁临时变量,即调用其析构函数
7)销毁x,即调用x的析构函数
但是为什么没有调用复制构造函数,也只有2次调用dtors,而我期望3。
我知道编译器可以进行优化,但是,我的理解是否正确?
非常感谢:)
此致
拉利
答案 0 :(得分:11)
按值返回 时,可能无法调用复制构造函数。一些编译器使用返回值优化功能。
了解“返回值优化”
答案 1 :(得分:4)
标准中告诉您编译器何时可以删除副本的部分是12.8 / 15。始终由编译器决定是否实际执行elision。有两种法律情况,加上它们的任意组合:
“在具有类返回类型的函数的return语句中,当表达式是具有与函数返回类型相同的cv-nonqualified类型的非易失性自动对象的名称时”
“当一个未绑定到引用的临时类对象被复制到具有相同cv-unqualified类型的类对象时”。
前者通常被称为“命名返回值优化”,它允许您在示例中看到的输出。后者实际上将复制初始化转换为直接初始化,例如,如果您的代码执行Example x = Example();
,则可能会发生。
除非通常的“假设”规则适用,否则不允许使用其他副本。因此,如果复制构造函数已经跟踪,则以下代码必须调用它:
Example x;
Example y = x;
但是如果x没有被使用,并且cctor没有副作用,那么我认为它可以被优化掉,就像任何其他无效的代码一样。
答案 2 :(得分:2)
做x = funct()时;编译器注意到它将被直接返回,从而避免了无用的构造。这也是为什么你只会得到两个析构函数的调用。
这就是为什么有时候使用“复制”并不一定会失去表演的原因。
答案 3 :(得分:0)
在你的例子中,结构足够小,因此它通过寄存器。生成的代码类似于Return value optimization。构建一个更复杂的示例,您将看到预期的行为。
答案 4 :(得分:0)
g ++ v4.4.1可以选择禁止“删除”优化:
tst@u32-karmic$ g++ -fno-elide-constructors Example.cpp -o Example tst@u32-karmic$ ./Example Default constructor called. Calling funct.. Default constructor called. Copy constructor called. Dtor invoked Assignment operator called. Dtor invoked Dtor invoked
正如您所见,现在调用了复制构造函数!