对于了解移动构造函数的工作原理,我们需要在编译时设置编译选项-fno-elide-constructors以关闭返回值优化效果,从样就可以看到完整的移动构造函数调用过程,避免编译器优化,有些地方看不到调用移动构造函数的过程。-fno-elide-constructors这个参数只是针对C++的,手册对该参数的描述如下:
-fno-elide-constructors
The C++ standard allows an implementation to omit creating a
temporary which is only used to initialize another object of the
same type. Specifying this option disables that optimization, and
forces G++ to call the copy constructor in all cases.
大致意思是:C++标准允许一种(编译器)实现省略创建一个只是为了初始化另一个同类型对象的临时对象。指定这个参数(-fno-elide-constructors)将关闭这种优化,强制G++在所有情况下调用拷贝构造函数。
(在stackoverflow上有一个有关这个参数使用的问题https://stackoverflow.com/questions/27086573/when-and-why-would-i-use-fno-elide-constructors)
下面就一段代码进行分析。
<textarea readonly="readonly" name="code" class="c++">
class A
{
public:
A() :m_ptr(new int(0)) { cout << "construct" << endl; }
A(const A& a) :m_ptr(new int(*a.m_ptr)) //深拷贝的拷贝构造函数
{
cout << "copy construct" << endl;
}
A(A&& a) :m_ptr(a.m_ptr)
{
a.m_ptr = nullptr;
cout << "move construct" << endl;
}
~A()
{
cout << "delete " << endl;
delete m_ptr;
}
private:
int* m_ptr;
};
A GetA()
{
A b;
return b;
}
int main() {
A a = GetA();
system("pause");
return 0;
}
</textarea>
使用g++ test.cpp -fno-elide-constructors -g -o .\.output\main.exe命令进行编译
运行结果:
construct //这是A b时调用无参构造函数
move construct //这是b初始化函数返回的临时对象时调用的移动构造函数
delete //释放b对象
move construct //将函数返回的临时对象初始化a时调用移动构造函数
delete //释放函数返回的临时对象
当将移动构造函数注释掉,编译后运行结果为:
construct /这是A b时调用无参构造函数
copy construct //这是b初始化函数返回的临时对象时调用的拷贝构造函数
delete //释放b对象
copy construct //将函数返回的临时对象初始化a时调用拷贝构造函数(因为自定义了拷贝构造函数,因此编译器不会合成默认 移动构造函数)
delete //释放掉函数返回的临时对象
当将移动构造函数和拷贝构造函数都注释掉,编译后运行结果为:
construct //这是A b时调用无参构造函数
delete //释放b对象
delete //释放掉函数返回的临时对象
注意第三种情况使用的是编译器自己合成的拷贝构造函数。因为没有自己定义拷贝构造函数或者拷贝赋值运算符。这里由于自定义了析构函数,因此编译器不会合成默认的移动构造函数。具体的可以参考《C++ primer》第13章。