当前位置:   article > 正文

C++的拷贝构造函数和移动构造函数_c++拷贝构造函数和移动构造

c++拷贝构造函数和移动构造

一、拷贝构造函数

当类没有定义拷贝构造函数的时候,编译器会默认提供一个,这个拷贝函数是浅拷贝

如果该类中含有指针,可能会发生内存泄漏,见下面的例子:

  1. class Test
  2. {
  3. public:
  4. int *p;
  5. Test(){ p=new int; };
  6. ~Test(){ delete p; };
  7. };
  8. void main()
  9. {
  10. Test t1;
  11. Test t2(t1);
  12. Test t3 = t1;
  13. }

t1、t2、t3的成员变量p指向的是同一块内存,程序结束后会出现重复释放的问题。

为了解决这个问题,可以自定义拷贝构造函数:

  1. class Test
  2. {
  3. public:
  4. int *p;
  5. Test(const Test &t)
  6. {
  7. p = new int (*(t.p));
  8. }
  9. Test(){ p=new int; };
  10. ~Test(){ delete p; };
  11. };

二、右值引用

除了上述的解决方法,还可以使用C++11的【右值引用】新特性来解决,而且可以提高程序的性能,减少内存开销。

为了引出左值引用的概念,先来复习左值和右值

1.左值和右值

int a = 3 + 4 ;

上面的式子中,变量 a 就是左值,右边的表达式会生成一个临时变量存放 (3+4) 的值,这个变量称之为右值。

有两种方式可以判断:

(1)只能放在等号(=)右侧的即为右值,可以放在左侧的为左值

int a = 10 ;
10 = a ; //错误

(2)左值可以取地址,而右值不允许:

int a = 3 + 4 ;
int * b = & a ;  //ok
b = & (3+4) ; //错误

2.右值引用

 使用方法如下,b就是对右值 (3+4) 的引用。

int && b = 3 + 4 ;

先看下下面的左值引用:

int a = 0 ;
int &b = 4 ; //错误!
int &b = a ; //左值引用

如上例所示,左值引用只能对左值进行别名引用,无法引用右值

于是C++11增加了右值引用,使用 && 表示(和逻辑运算中的”且“一致)。

int a = 0 ;
int b = 1 ;
int && c = a+c ; //右值引用
int && c = 3 ; //右值引用
int && c = 3 +4 ; //右值引用
int && c = a ; //错误!

注意不能直接右值引用左值,C++提供了一个函数std::move()函数,可以将左值变成右值:

string str1 = "aa" ;
string && str2 = std::move( str1 );  //ok

3.右值引用的应用场景

(1)案例:

还是回到之前的例子:

  1. class Test
  2. {
  3. public:
  4. int *p;
  5. Test(const Test &t)
  6. {
  7. p = new int (*(t.p)); cout<<"copy construct"<<endl;
  8. }
  9. Test(){ p=new int; cout<<"construct"<<endl; };
  10. ~Test(){ delete p; cout<<"destruct"<<endl; };
  11. };
  12. Test getTest()
  13. {
  14. return Test();
  15. }
  16. void main()
  17. {
  18. {
  19. Test t = getTest();
  20. }
  21. }

使用vs2012运行,结果为:

construct                 //执行 Test()
destruct                  //销毁 t

 但需要注意的是,这是vs编译器对拷贝构造函数优化后的结果。禁止优化,结果为:

construct                 //执行 Test()
copy construct            //执行 return Test()
destruct                  //销毁 Test() 产生的匿名对象
copy construct            //执行 t = getTest()
destruct                  //销毁 getTest() 返回的临时对象
destruct                  //销毁 t

可以看到,进行了两次的深拷贝,对于对内存要求不高、本例这种占内存比较小的类Test而言(申请的堆空间小),可以接受。

但如果临时对象中的指针成员申请了大量的堆空间,那将严重影响程序的执行效率。

C++11为了解决这一问题(深拷贝占用大量空间),引入移动构造函数

 (2)移动构造函数

所谓的移动,就是将其他的内存资源,“移为己有”,这些资源通常是临时对象,比如上文所叙的右值

修改如下(增加一个移动构造函数):

  1. class Test
  2. {
  3. public:
  4. int *p;
  5. Test(Test &&t) //移动构造函数
  6. {
  7. p = t.p;
  8. t.p = nullptr;//将临时对象的指针赋值为空
  9. cout<<"copy construct"<<endl;
  10. }
  11. Test(const Test &t) //拷贝构造函数
  12. {
  13. p = new int (*(t.p));
  14. cout<<"move construct"<<endl;
  15. }
  16. Test(){ p=new int; cout<<"construct"<<endl; };
  17. ~Test(){ delete p; cout<<"disconstruct"<<endl; };
  18. };
  19. Test getTest()
  20. {
  21. return Test();
  22. }
  23. void main()
  24. {
  25. {
  26. Test t = getTest();
  27. }
  28. }

禁止vs优化,结果为:

construct                 //执行 Test()
move construct            //执行 return Test()
destruct                  //销毁 Test() 产生的匿名对象
move construct            //执行 t = getTest()
destruct                  //销毁 getTest() 返回的临时对象
destruct                  //销毁 t

可以看到,定义了移动构造函数后,临时对象的创建使用移动构造函数创建,如下,没有在堆上创建对象,减少了开销。

 Test(Test &&t) //移动构造函数
 {
     p = t.p;
     t.p = nullptr;//将临时对象的指针赋值为空
     cout<<"copy construct"<<endl;
 }

原文出处:拷贝构造函数与移动构造函数 - Hosseini - 博客园一、拷贝构造函数 当类没有定义拷贝构造函数的时候,编译器会默认提供一个,这个拷贝函数是浅拷贝。 如果该类中含有指针,可能会发生内存泄漏,见下面的例子: class Test { public: inthttps://www.cnblogs.com/hosseini/p/15089358.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/178392?site
推荐阅读
相关标签
  

闽ICP备14008679号