上篇C++57个入门知识点_23_ 拷贝构造函数(利用一个对象创建另一个对象,调用的构造函数即拷贝构造函数;缺省下为完全拷贝;手写即CStudent(CStudent& obj) {…}-对象做参数)介绍了拷贝构造函数的使用,本篇将会介绍拷贝构造函数有哪些需要注意的事项。
总结:
1. 浅拷贝: 是将对象1拷贝给对象2,对象1、2均指向一块内存,会存在重复释放导致程序崩溃的问题
2. 深拷贝: 自己写拷贝构造函数重新分配内存,将对象1的内容复制给对象2,避免重复释放问题
3. 何时重写拷贝构造函数: 当类对象中的成员不是基本数据类型,存在一种需要分配的资源时(堆空间,例如使用:m_szName = (char*)malloc(255)
创建的动态内存
、或者可能是文件的句柄
、或者其他分配的资源
),为了避免在析构时重复释放,需要重写拷贝构造函数或禁用
4. 基本数据类型不存数据释放的问题,只有资源才会涉及内存申请和释放
。
上篇我们使用默认的构造函数,利用一个对象创建另一个对象时,运行过程中由于原对象和创建的对象指向的内容完全一致,在析构时由于要析构两次,第二次析构时由于第一次析构已经将内存释放,再去析构就出现了程序崩溃的问题。
1. 浅拷贝
缺省的拷贝构造函数即为浅拷贝,其作用为:
- 完全把对象1拷贝给对象2;
- 内存的拷贝(浅拷贝,对象1、2均指向一块内存)
浅拷贝在释放时会两次释放同一块内存,因此在第二次释放时由于内存已经释放过一次,因此会造成崩溃
浅拷贝的拷贝构造函数写法:
CStudent(CStudent& obj) {
printf("CStudent(CStudent& obj) \r\n");
//浅拷贝:将对象1的值赋值给对象2
//如果只是基本数据类型就不存数据释放的问题,而此处是一个资源,涉及内存的申请和释放,下句的含义是对象1和2都指向同一个内存块
this->m_szName = obj.m_szName;
}
2. 深拷贝
深拷贝就可以解决上面浅拷贝析构两次导致程序崩溃的问题,自己写拷贝构造函数重新分配内存,将对象1的内容复制给对象2
。
一张图看懂:
深拷贝的拷贝构造函数的写法:
//通过引用的方式,将一个对象拷贝给另一个对象
CStudent(CStudent& obj) {
printf("CStudent(CStudent& obj) \r\n");
//重新分配空间
m_szName = (char*)malloc(255);//c语言中在堆上创建动态内存
if (m_szName == nullptr) {
return;
}
//深拷贝,把对象1的内容复制给对象2
memcpy(this->m_szName,obj.m_szName,255);
}
运行结果:对象1和2内容一样,但是指向了不同的堆空间地址,在进行析构的时候就会分别进行析构,从而避免浅拷贝出现的问题
3. 何时需要重写拷贝构造函数
当类对象中的成员函数存在一种需要分配的资源时(堆空间,例如使用:
m_szName = (char*)malloc(255)
创建的动态内存、或者可能是文件的句柄、或者其他分配的资源),为了避免在析构时重复释放,需要重写拷贝构造函数或禁用。
4.学习视频地址:C++57个入门知识点_24_ 浅拷贝与深拷贝
5.程序代码
#include <iostream>
class CStudent {
public:
CStudent() {
printf("CStudent()\r\n");
//为了m_szNam创建一个堆空间(分配动态内存)
m_szName = (char*)malloc(255);
}
//为了初始化方便,使用带有参数的构造
CStudent(const char* pName, int nlength) {
printf("CStudent(char* pName, int nlength)\r\n");
//判断缓存要求是否大于255,大于255就返回
if (nlength > 255) {
return;
}
//为m_szNam创建一个堆空间(分配动态内存)
m_szName = (char*)malloc(255);//c语言中在堆上创建动态内存
//判断堆上创建的内存是否为空,空则返回
if (m_szName == nullptr) {
return;
}
memcpy(m_szName, pName, nlength);
}
//通过引用的方式,将一个对象拷贝给另一个对象
CStudent(CStudent& obj) {
printf("CStudent(CStudent& obj) \r\n");
//浅拷贝
//如果为普通数据类型这种是没有问题,但是此处是资源,涉及内存的申请和释放,下句的含义是指向同一个内存块
//this->m_szName = obj.m_szName;
//重新分配空间
m_szName = (char*)malloc(255);//c语言中在堆上创建动态内存
if (m_szName == nullptr) {
return;
}
//深拷贝,把对象1的内容复制给对象2
memcpy(this->m_szName, obj.m_szName, 255);
}
~CStudent() {
free(m_szName);
printf("~CStudent()\r\n");
}
void SetName(const char* pszName){}
private:
int m_nStudID;//学号
char* m_szName;
};
int main(int argc, char* argv[])
{
//创建对象进行构造,strlen("张三")+1是为了包含结尾
CStudent stu("张三", strlen("张三") + 1);
//利用已经定义的对象定义另一个对象
CStudent stu2 = stu; //将stu对象赋值给stu2 类似于int n=1;int k=n;
return 0;
}