拷贝构造函数和移动构造函数
C++ Primer 里讲拷贝控制和资源管理的时候,分了两小节,分别叫行为像值的类和行为像指针的类。
行为像值的类,即类对象的资源由自身独占,不与其它类对象共享。当用一个已有的类对象 a 拷贝初始化一个新的类对象 b 时,调用拷贝构造函数(这里其实涉及很多内容,如 explicit constructor、implicit conversion、explicit conversion、allocator、编译器优化等),对象 a 的数据成员全部深拷贝到对象 b。
行为像指针的类,即类对象的资源可以与其他的类对象共享。当用一个已有的类对象 a 拷贝初始化一个新的类对象 b 时,调用拷贝构造函数,对象 a 的数据成员全部浅拷贝到对象 b。
根据类的行为是像值还是像指针,对应了拷贝构造函数是执行了深拷贝还是浅拷贝。而移动构造函数,其实就是将源对象的数据成员浅拷贝到新对象,并且将源对象的指针数据成员置空。
对于行为像指针的类,由于拷贝构造函数本身是做浅拷贝的,此时构建新对象是调用拷贝构造函数还是移动构造函数其实效率无差。
对于行为像值的类,构建新对象是调用拷贝构造函数还是移动构造函数效率差别就在于深拷贝和浅拷贝的不同了,拷贝的资源越大,拷贝的次数越多,移动构造函数带来的效率优化就越大。
当用右值对象去拷贝初始化新对象时,编译器会选用移动构造函数,因为右值对象在接下来也不再需要了,我们完全可以浅拷贝掠夺它的资源,免去深拷贝带来的消耗。当用一个左值对象 a 去拷贝初始化新对象时,编译器会选用拷贝构造函数,如果我们确定这个左值之后不再使用,不防用 std::move(a) 初始化新对象,这样编译器就会选用移动构造函数。
移动、右值引用等特性是 C++ 11 引入的,是 C++标准下为性能提升提供的手段。而有时候,编译器在传入参数、返回值上做的优化甚至比使用移动语义还要更加强大,但是编译器优化不属于 C++标准的范畴,各编译器厂商提供的优化程度可能不一致,我们不能过于依赖于编译器优化。