深复制与浅复制
C++中,默认的复制构造函数只能实现浅复制。
浅复制指的是在对象复制前,只对对象中的数据成员进行简单的复制
大多数情况下"浅复制"已经能很好的工作了,但是当类的数据成员中有指针类型时,浅复制只会复制指针的值(地址),这样会导致两个成员指针指向同一块内存,从而带来数据安全方面的隐患,如需要分别用delete
释放指针所指向的空间时就会出现问题。为了实现正确的复制,此时我们必须编写复制构造函数进行深复制。
深复制指的是当类的成员变量有指针类型时,复制对象时应该为指针变量重新分配一个新的内存空间,使该指针指向这个新的内存空间,避免浅复制中只复制指针的值,使两个指针指向同一块内存。
浅复制和深复制主要的区别就是复制指针时是否创建内存空间 ,如果没有创建内存只复制地址为浅复制,创建新内存并把值全部复制一份就是深复制。采用深复制的情况下,释放内存的时候就不会出现浅复制时重复释放同一内存的错误。
下面是对象采用浅复制的一个案例!
#include <iostream>
#include <string.h>
using namespace std;
class Cperson{
private:
int m_age;
char *m_name;
public:
Cperson(int age,char *name);
~Cperson();
void Print(void);
};
Cperson::Cperson(int age,char *name){
m_name = new char[strlen(name)+1];
if(m_name!=NULL){
strcpy(m_name,name);
}
m_age = age;
cout<<m_age<<"的构造函数"<<endl;
}
Cperson::~Cperson(){
cout<<"析构姓名:"<<m_name<<endl;
if(m_name!=NULL){
delete []m_name;
}
}
void Cperson::Print(void){
cout<<"My age is "<<m_age<<",My name is "<<m_name<<endl;
}
int main(void)
{
Cperson Tom(10,"Tom");
Tom.Print();
Cperson Jim(Tom);
Jim.Print();
return 0;
}
运行结果如下:
程序运行的结果出现了错误。这是因为在执行语句
CPerson Tom(10,"TOM");
用动态开辟了一块内存,用来存放"Tom"。而在执行
Cperson Jim(Tom);
时,调用的是默认构造复制函数,实现对应数据的直接复制,即将Tom的成员(Tom.m_age,Tom.m_name)
赋值给Jim相应的成员。此时,Tom.m_name
和 Jim.m_name
指向同一块内存,然而系统并没有给 Jim.m_name
开辟相应的内存空间。执行完 Jim.Print()
之后,开始执行析构函数,析构函数的执行顺序和对象构造函数的执行顺序相反,所以先执行Jim的析构函数,执行完Jim的析构函数之后,Jim.m_name
所指向的空间已经释放。接着执行Tom的析构函数,此时就会出现问题,即在释放Tom.m_name
所指向的内存空间时会出现问题,因为这段内存空间在Jim的析构函数里已经释放过了。出现这种问题的根本原因在于默认复制构造函数实现的是"浅复制",所以需要定义自己的复制构造函数实现"深复制" ,就可以避免上述问题。下面的代码进行深复制的展示:
/*对象的深复制*/
#include <iostream>
#include <string.h>
using namespace std;
class Cperson{
private:
int m_age;
char *m_name;
public:
Cperson(int age,char *name);
Cperson(Cperson &per);
~Cperson();
void Print(void);
};
Cperson::Cperson(int age,char *name){
m_name = new char[strlen(name)+1];
if(m_name!=NULL){
strcpy(m_name,name);
}
m_age = age;
cout<<m_name<<"的构造函数"<<endl;
}
Cperson::Cperson(Cperson &per){
m_name = new char[strlen(per.m_name)+1];
if(m_name != NULL){
strcpy(m_name,per.m_name);
}
m_age = per.m_age;
cout<<m_name<<"的拷贝构造函数"<<endl;
}
Cperson::~Cperson(){
cout<<"析构姓名:"<<m_name<<endl;
if(m_name!=NULL){
delete []m_name;
}
}
void Cperson::Print(void){
cout<<"My age is "<<m_age<<",My name is "<<m_name<<endl;
}
int main(void)
{
Cperson Tom(10,"Tom");
Tom.Print();
Cperson Jim(Tom);
Jim.Print();
return 0;
}
运行结果如下:
总结:
不知道大家看完之后是否已经理解,其实浅复制与深复制之间的关系除了是否为指针变量开辟新的空间之外,还有一个理解角度就是:浅复制无需手动添加拷贝构造函数,深复制需要手动添加拷贝构造函数。