在c++11标准出来前,想用其它对象初始化一个同类新对象,只能借助类的拷贝构造函数。但是实际上可能会又多次拷贝,导致对象内部的指针成员每次深拷贝都会申请大量堆空间,势必会影响对象的初始化执行效率,然后就引入了移动构造函数,进行避免这样的无效多次拷贝。
看例子

  1. #include <iostream>
  2. using namespace std;
  3. class demo{
  4. public:
  5. demo():num(new int(0)){
  6. cout<<"construct!"<<endl;
  7. }
  8. //拷贝构造函数
  9. demo(const demo &d):num(new int(*d.num)){
  10. cout<<"copy construct!"<<endl;
  11. }
  12. ~demo(){
  13. cout<<"class destruct!"<<endl;
  14. }
  15. private:
  16. int *num;
  17. };
  18. demo get_demo(){
  19. return demo();
  20. }
  21. int main(){
  22. demo a = get_demo();
  23. return 0;
  24. }

执行时使用禁止优化命令 g++ demo.cpp -fno-elide-constructors
输出为

  1. construct!
  2. copy construct!
  3. class destruct!
  4. copy construct!
  5. class destruct!
  6. class destruct!

可以看到先是 get_demo() 方法中的 demo() 执行了构造,然后赋给临时对象demo,这个过程进行了一次拷贝,然后demo() 对象执行了析构,然后临时对象赋值给 a 的时候,又执行了 拷贝。

如果我们添加一个移动构造函数,试试效果会如何

  1. #include <iostream>
  2. using namespace std;
  3. class demo{
  4. public:
  5. demo():num(new int(0)){
  6. cout<<"construct!"<<endl;
  7. }
  8. //拷贝构造函数
  9. demo(const demo &d):num(new int(*d.num)){
  10. cout<<"copy construct!"<<endl;
  11. }
  12. //添加移动构造函数
  13. demo(demo &&d):num(d.num){
  14. d.num = NULL;
  15. cout<<"move construct!"<<endl;
  16. }
  17. ~demo(){
  18. cout<<"class destruct!"<<endl;
  19. }
  20. private:
  21. int *num;
  22. };
  23. demo get_demo(){
  24. return demo();
  25. }
  26. int main(){
  27. demo a = get_demo();
  28. return 0;
  29. }

输出效果

  1. construct!
  2. move construct!
  3. class destruct!
  4. move construct!
  5. class destruct!
  6. class destruct!

从之前的两次拷贝,换成了走移动构造函数,为什么呢?
当类中同时包含拷贝构造函数和移动构造函数时,如果使用临时对象初始化当前类的对象,编译器会优先调用移动构造函数来完成此操作。只有当类中没有合适的移动构造函数时,编译器才会退而求其次,调用拷贝构造函数。
我们get_demo()返回的就是临时对象,这是一个右值。即右值来初始化对象将会调用移动拷贝构造函数。

这种写法的好处在于?

num指针会被深拷贝,每次走拷贝构造函数都要重新申请一个对空间,势必影响执行效率。
get_demo()方法创建的对象到我们使用的 a对象上,进过了2次拷贝,每次都深拷贝,有必要吗?没有。我们这块浅拷贝就行了,是不是?那就用移动拷贝构造函数,使用中需要避免同一块空间被释放多次的情况。

左值初始化对象如何调用移动构造函数?move

move 函数也是 c++11引入的新特性
直接看例子把

  1. #include <iostream>
  2. using namespace std;
  3. class first {
  4. public:
  5. first() :num(new int(0)) {
  6. cout << "construct!" << endl;
  7. }
  8. //移动构造函数
  9. first(first &&d) :num(d.num) {
  10. d.num = NULL;
  11. cout << "first move construct!" << endl;
  12. }
  13. public: //这里应该是 private,使用 public 是为了更方便说明问题
  14. int *num;
  15. };
  16. class second {
  17. public:
  18. second() :fir() {}
  19. //用 first 类的移动构造函数初始化 fir
  20. second(second && sec) :fir(move(sec.fir)) {
  21. cout << "second move construct" << endl;
  22. }
  23. public: //这里也应该是 private,使用 public 是为了更方便说明问题
  24. first fir;
  25. };
  26. int main() {
  27. second oth;
  28. second oth2 = move(oth);
  29. //cout << *oth.fir.num << endl; //程序报运行时错误
  30. return 0;
  31. }

输出结果

  1. construct!
  2. first move construct!
  3. second move construct

oth 为左值,想要调移动构造,必须通过move生一个oth的右值版本。
oth对象内包含了一个first对象,对于first来说也是左值,如果first也想要调用移动,那么也要调用move生成一个右值版本