我们使用gcc编译器,C++11,
《c++核心指南》的一个条款:“For “out” output values, prefer return values to output parameters”:在函数输出数值时,尽量使用返回值而非输出参数。
我们之前的做法应该是函数参数里面使用指针或者引用,而不是返回值,为什么现在推荐使用返回值?
返回非引用类型的表达式结果是个纯右值(prvalue)。在执行 Obj r = … 的时候,编译器会认为我们实际是在构造 Obj r(…),而“…”部分是一个纯右值。因此编译器会首先试图匹配 Obj(Obj&&),在没有时则试图匹配 Obj(const Obj&);也就是说,有移动支持时使用移动,没有移动支持时则拷贝。
1. 首先看一个例子:
class Obj
{
public:
Obj() {std::cout << "Obj()" << std::endl;}
~Obj() { std::cout << "~Obj()" << std::endl; }
Obj(const Obj &) {std::cout << "copy obj" << std::endl;}
Obj(Obj &&) {std::cout << "move obj" << std::endl;}
};
Obj getObj()
{
return Obj();
}
int main()
{
auto a = getObj();
}
首先思考一下,是不是觉得这个例子的输出应该有“copy obj“或者“move obj”。但其实只输出两行:
Obj()
~Obj()
这里的最主要原因是编译器给我们做了返回值优化。
2. 稍微变动一下代码:
Obj getObj()
{
Obj a;
return a;
}
输出仍然是一样。但在其他编译器下有可能会调用移动构造函数。
3. 继续改动一下代码:
Obj getObj(int n)
{
Obj a1;
Obj a2;
if (n > 10) {
return a1;
} else {
return a2;
}
}
int main()
{
auto a = getObj(5);
}
这次的输出是:
Obj()
Obj()
move obj
~Obj()
~Obj()
~Obj()
可以看到,这里使用了移动构造函数。
4. 接下来把移动构造函数删除:
class Obj
{
public:
Obj() {std::cout << "Obj()" << std::endl;}
~Obj() { std::cout << "~Obj()" << std::endl; }
Obj(const Obj &) {std::cout << "copy obj" << std::endl;}
};
可以看到输出:
Obj()
Obj()
copy obj
~Obj()
~Obj()
~Obj()
可以看到,调用了拷贝构造函数。
5. 再进一步,删除拷贝构造函数:
因为编译器会默认提供拷贝构造函数和移动构造函数,我们应该使用delete,不能简单的注释掉。
class Obj
{
public:
Obj() {std::cout << "Obj()" << std::endl;}
~Obj() { std::cout << "~Obj()" << std::endl; }
Obj(const Obj &) = delete;
};
可以看到,上面的函数不能正常工作。
编译器不是会提供默认移动构造函数吗,为什么第4步和第5步中还是调用了拷贝构造函数?
根据c++标准:如果用户声明了一个显式的拷贝构造函数,即使声明中使用了delete
,都不会有隐式声明的移动构造函数被生成。也就是说,如果一个类X没有显式的声明移动构造函数,那么当且仅当用户没有提供拷贝构造函数时,才会生成默认的移动构造函数。
参考:
https://stackoverflow.com/questions/23771194/why-should-i-delete-move-constructor-and-move-assignment-operator-in-a-singleton
标签:Obj,编译器,返回值,拷贝,移动,优化,构造函数 From: https://www.cnblogs.com/errorman/p/17254972.html