【C++】拷贝构造函数 Copy Constructor (深拷贝、浅拷贝、赋值操作符)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_40416052/article/details/82734346

一、拷贝构造函数的性质

定义:

  拷贝构造函数是一个成员函数,它用同一个类的一个对象初始化另一个对象。

语法:


 
 
  1. classname ( const classname &obj) {
  2. // body of constructor
  3. }

 note: 当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值赋值。

拷贝构造函数的作用:

1.从同一类型的另一个对象初始化一个对象。
2.将对象拷贝作为一个函数的参数。
3.拷贝一个对象以返回它的函数。

拷贝构造函数的意义:

浅拷贝拷贝后对象的物理状态相同
深拷贝拷贝后对象的逻辑状态相同

note:编译器提供的拷贝构造函数只进行浅拷贝。

什么时候调用拷贝构造函数:

1 .当类对象返回一个值;
2.当类的对象被作为参数通过值传递(到函数)时;
3.当一个类的对象是基于同一个类的另一个对象构造时;
4.当编译器生成一个临时对象时

 

Example1:


 
 
  1. #include <iostream>
  2. using namespace std;
  3. class Line {
  4. public:
  5. int getLength(void);
  6. Line( int len); // 普通构造函数
  7. Line( const Line &obj); // 拷贝构造函数
  8. ~Line(); // 析构函数
  9. private:
  10. int *ptr;
  11. };
  12. // 成员函数定义,包括构造函数
  13. Line::Line( int len) {
  14. cout << "普通构造函数分配指针ptr" << endl;
  15. // 给指针ptr分配内存;
  16. ptr = new int;
  17. *ptr = len;
  18. }
  19. Line::Line( const Line &obj) { //obj是用于初始化另一个对象的对象的引用。
  20. cout << "拷贝构造函数分配指针 ptr." << endl;
  21. ptr = new int;
  22. *ptr = *obj.ptr; // 拷贝值
  23. }
  24. Line::~Line( void) {
  25. cout << "释放内存!" << endl;
  26. delete ptr;
  27. }
  28. int Line::getLength( void) {
  29. return *ptr;
  30. }
  31. void display(Line obj) {
  32. cout << "line 的长度 : " << obj.getLength() << endl;
  33. }
  34. // Main 函数实现
  35. int main() {
  36. Line line(10); //调用普通构造函数 Line(int len);
  37. display(line); //调用拷贝构造函数 Line(const Line &obj);
  38. getchar();
  39. return 0;
  40. }

 编译运行,结果如下:

调用拷贝构造函数的另一种方式 Line line2 = line1; 如下:


 
 
  1. int main() {
  2. Line line1(10);
  3. Line line2 = line1; // 这也是调用拷贝构造函数
  4. display(line1);
  5. display(line2);
  6. getchar();
  7. return 0;
  8. }

 二、深拷贝与浅拷贝

Example2:


 
 
  1. #include <stdio.h>
  2. class Test
  3. {
  4. private:
  5. int i;
  6. int j;
  7. int* p;
  8. public:
  9. int getI()
  10. {
  11. return i;
  12. }
  13. int getJ()
  14. {
  15. return j;
  16. }
  17. int* getP()
  18. {
  19. return p;
  20. }
  21. Test( const Test& t) //拷贝构造函数
  22. {
  23. i = t.i;
  24. j = t.j;
  25. p = new int;
  26. *p = *t.p;
  27. }
  28. Test( int v)
  29. {
  30. i = 1;
  31. j = 2;
  32. p = new int;
  33. *p = v;
  34. }
  35. void free()
  36. {
  37. delete p;
  38. }
  39. };
  40. int main()
  41. {
  42. Test t1(3); //调用构造函数Test(int v)
  43. Test t2(t1); //调用拷贝构造函数Test(const Test& t)
  44. printf( "t1.i = %d, t1.j = %d, *t1.p = %d\n", t1.getI(), t1.getJ(), t1.getP()); //*t1.p = 4887464
  45. printf( "t2.i = %d, t2.j = %d, *t2.p = %d\n", t2.getI(), t2.getJ(), t2.getP()); //*t2.p = 4887512
  46. t1. free();
  47. t2. free();
  48. getchar();
  49. return 0;
  50. }

编译运行, 

可见,t1 和 t2所指的地址空间是不同的 ,因为t2进行深拷贝。

我们不妨把注释掉下列代码:


 
 
  1. Test( const Test& t) //拷贝构造函数
  2. {
  3. i = t.i;
  4. j = t.j;
  5. p = new int;
  6. *p = *t.p;
  7. }

然后编译运行:

可见,指针 t1 和 t2的地址相同,说明了t1 和t2 都指向了同一片内存空间。

 问题分析:

①浅拷贝:默认构造函数只做浅拷贝。

t1 和t2 都指向了同一片内存空间:

                                                       

 当释放内存地址时,t1 和 t2释放的是同一块内存地址,此时会发生编译错误:

 ②深拷贝:只能与用户定义的复制构造函数。在用户定义的复制构造函数中,我们确保复制对象指向新的内存位置的指针(或引用)。

                                                        

可见,Bag2 深拷贝 Bag1,Bag2指向了一片新的内存地址。

Question: 什么时候需要进行深拷贝?

当对象中有成员指代了系统中的资源:

1.成员指向了动态内存空间
2.成员打开了外存中的文件
3.成员使用了系统中的网络端口

一般性原则:

  自定义拷贝构造函数,必然需要实现深拷贝!

 

三、拷贝构造函数 VS 赋值操作符

下面的两个语句哪个调用拷贝构造函数,哪个调用赋值操作符?


 
 
  1. MyClass t1, t2;
  2. MyClass t3 = t1; // ----> (1)
  3. t2 = t1; // -----> (2)

答:

当一个已经初始化的对象从另一个现有对象分配一个新值时,调用赋值操作符:

t2 = t1;  //调用赋值操作符, 相当于 "t2.operator=(t1);"

当新对象从现有对象创建时,就会调用拷贝构造函数,作为现有对象的拷贝:

Test t3 = t1;  // 调用拷贝构造函数, 相当于"Test t3(t1);"

 Test:


 
 
  1. #include<iostream>
  2. #include<stdio.h>
  3. using namespace std;
  4. class Test
  5. {
  6. public:
  7. Test() {}
  8. Test( const Test &t)
  9. {
  10. cout << "调用拷贝构造函数 " << endl;
  11. }
  12. Test& operator = ( const Test &t)
  13. {
  14. cout << "调用赋值操作符 " << endl;
  15. }
  16. };
  17. int main()
  18. {
  19. Test t1, t2;
  20. t2 = t1; //调用赋值操作符
  21. Test t3 = t1; //调用赋值操作符
  22. getchar();
  23. return 0;
  24. }

编译运行,结果如下:

编译运行,结果如下:

 

<本文完> 

Reference:

1)www.tutorialspoint.com

2)www.geeksforgeeks.org

3)唐佐林《C++深度解析教程》 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值