博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
对于拷贝构造函数和赋值构造函数的理解
阅读量:5066 次
发布时间:2019-06-12

本文共 3583 字,大约阅读时间需要 11 分钟。

昨天晚上在看智能指针的时候,我发现自己连一个拷贝构造函数和赋值构造函数都写不出来,自己就尝试写了一个版本,结果发现错误百出,对于拷贝构造函数和赋值构造函数的理解仅仅停留在理论的方面,而不知其中太多的内涵。

比如我们都知道拷贝构造函数和赋值构造函数最大的不同在于:

 

拷贝构造是确确实实构造一个新的对象,并给新对象的私有成员赋上参数对象的私有成员的值,新构造的对象和参数对象地址是不一样的,所以如果该类中有一个私有成员是指向堆中某一块内存,如果仅仅对该私有成员进行浅拷贝,那么会出现多个指针指向堆中同一块内存,这是会出现问题,如果那块内存被释放了,就会出现其他指针指向一块被释放的内存,出现未定义的值的问题,如果深拷贝,就不会出现问题,因为深拷贝,不会出现指向堆中同一块内存的问题,因为每一次拷贝,都会开辟新的内存供对象存放其值。

下面是浅拷贝构造函数的代码:

#include 
using namespace std;class A{private: int* n;public: A() { n = new int[10]; n[0] = 1; cout<<"constructor is called\n"; } A(const A& a) { n = a.n; cout<<"copy constructor is called\n"; } ~A() { cout<<"destructor is called\n"; delete n; } void get() { cout<<"n[0]: "<
<

运行结果如下:

 

下面是深拷贝构造函数的代码:

#include 
#include
using namespace std;class A{private: int* n;public: A() { n = new int[10]; n[0] = 1; cout<<"constructor is called\n"; } A(const A& a) { n = new int[10]; memcpy(n, a.n, 10); //通过按字节拷贝,将堆中一块内存存储到另一块内存 cout<<"copy constructor is called\n"; } ~A() { cout<<"destructor is called\n"; delete n; } void get() { cout<<"n[0]: "<
<

运行截图如下:

 

 

但是赋值构造函数是将一个参数对象中私有成员赋给一个已经在内存中占据内存的对象的私有成员,赋值构造函数被赋值的对象必须已经在内存中,否则调用的将是拷贝构造函数,当然赋值构造函数也有深拷贝和浅拷贝的问题。当然赋值构造函数必须能够处理自我赋值的问题,因为自我赋值会出现指针指向一个已经释放的内存。还有赋值构造函数必须注意它的函数原型,参数必须是引用类型,返回值也必须是引用类型,否则在传参和返回的时候都会再次调用一次拷贝构造函数。

 

#include 
#include
using namespace std;class A{private: int* n;public: A() { n = new int[10]; n[0] = 1; cout<<"constructor is called\n"; } A(const A& a) //拷贝构造函数的参数一定是引用,不能不是引用,不然会出现无限递归 { n = new int[10]; memcpy(n, a.n, 10); //通过按字节拷贝,将堆中一块内存存储到另一块内存 cout<<"copy constructor is called\n"; } A& operator=(const A& a) //记住形参和返回值一定要是引用类型,否则传参和返回时会自动调用拷贝构造函数 { if(this == &a) //为什么需要进行自我赋值判断呢?因为下面要进行释放n的操作,如果是自我赋值,而没有进行判断的话,那么就会出现讲一个释放了的内存赋给一个指针 return *this; if(n != NULL) { delete n; n == NULL; //记住释放完内存将指针赋为NULL } n = new int[10]; memcpy(n, a.n, 10); cout<<"assign constructor is called\n"; return *this; } ~A() { cout<<"destructor is called\n"; delete n;      n = NULL; //记住释放完内存将指针赋为NULL } void get() { cout<<"n[0]: "<
<
get();return 0;}

运行截图如下:

如果我们在赋值构造函数的形参和返回值不用引用类型,代码如下:

#include 
#include
using namespace std;class A{private: int* n;public: A() { n = new int[10]; n[0] = 1; cout<<"constructor is called\n"; } A(const A& a) //拷贝构造函数的参数一定是引用,不能不是引用,不然会出现无限递归 { n = new int[10]; memcpy(n, a.n, 10); //通过按字节拷贝,将堆中一块内存存储到另一块内存 cout<<"copy constructor is called\n"; } A operator=(const A a) //传参和返回值设置错误 { if(this == &a) return *this; if(n != NULL) { delete n; n == NULL; } n = new int[10]; memcpy(n, a.n, 10); cout<<"assign constructor is called\n"; return *this; } ~A() { cout<<"destructor is called\n"; delete n; } void get() { cout<<"n[0]: "<
<
get(); while(1) {} return 0;}

运行截图如下:

 

多了两次的拷贝构造函数的调用和两次析构函数的调用。

 

转载于:https://www.cnblogs.com/GODYCA/archive/2013/01/16/2862885.html

你可能感兴趣的文章
php实现隐藏字符串的功能
查看>>
设计模式08: Composite 组合模式(结构型模式)
查看>>
编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试...
查看>>
公网IP和私有IP的区别和用途
查看>>
在一台win10上启动多个mysql
查看>>
TensorFlow 从零到helloWorld
查看>>
@class、#import
查看>>
iOS 正则表达式使用的三种方式&语法
查看>>
kafka的使用
查看>>
AT2672 Coins
查看>>
团队计划会议-01
查看>>
Linux0.11内核--加载可执行二进制文件之1.copy_strings
查看>>
编写Nginx启停服务脚本
查看>>
这些老外的开源技术养活了很多国产软件
查看>>
看图软件推荐
查看>>
【IdentityServer4文档】- 欢迎来到 IdentityServer4
查看>>
安全测试的一些漏洞和测试方法
查看>>
spring框架学习笔记(八)
查看>>
vim格式化代码
查看>>
探索 ConcurrentHashMap 高并发性的实现机制
查看>>