简介
浅拷贝
同一类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为浅拷贝.
一般情况下,浅拷贝没有任何副作用,但是当类中有指针,并且指针指向动态分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题。
深拷贝
当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间,深拷贝。
1.默认拷贝构造函数下,使用有参构造函数,不会出现问题。
//version1.0
#include <iostream>
#include <cstring>
using namespace std;
//动态申请内存的时候一定要手动创建析构函数进行释放,以免造成内存泄漏
class person
{
public:
//有参构造函数
person(char* name,int age)
{
pname = (char *)malloc(strlen(name) + 1);
strcpy(pname, name);
mage = age;
}
//动态申请内存的时候,数据存在堆区,如果不释放,指向该内存的指针一旦改变指向(地址)
//那么这段内存空间将无法访问也无法释放,就造成了内存泄漏,所以有动态申请内存时,一定要手动析构
~person()
{
cout<<"调用析构函数"<<endl;
if(NULL != pname)
{
free(pname);
pname = NULL;
}
}
char *pname;
int mage;
};
int main()
{
person p1("张三", 66);
cout << "name=" << p1.pname << " age=" << p1.mage<<endl;
}
使用有参构造函数的时候只要析构函数记得释放申请的内存就行。
2.使用默认拷贝函数出现问题
#include <iostream>
#include <cstring>
using namespace std;
class person
{
public:
//有参构造函数,name是指针传递
person(char *name,int age)
{
pname = (char *)malloc(strlen(name)+1);
strcpy(pname,name);
mage = age;
}
//拷贝构造函数,默认是浅拷贝,就是值拷贝。
//比如这里的pname接受的是地址,这里的值相同是指地址相同
//即新创建的对象和源对所指向的字符串是同一块内存
//当main函数结束,p1先调用析构函数释放一次
//p2再调用析构函数释放一次,但是此时该楚内存已经在释放对象p1时释放了
//这样就存在对已经释放的内存重复释放,程序崩溃
//析构函数
~person()
{
cout<<"调用析构函数"<<endl;
//暂停,以便在程序崩溃之前看到打印信息
cin.get();
if(NULL != pname)
{
free(pname);
pname = NULL;
}
}
char *pname;
int mage;
};
int main()
{
person p1("张三",66);
person p2(p1);
//通过打印可以看到一些信息
cout<<"p1.name="<<p1.pname<<" p1.mage="<<p1.mage<<" p1.name的地址:"<<(int*)p1.pname<<endl;
cout<<"p1.name="<<p1.pname<<" p1.mage="<<p1.mage<<" p2.name的地址:"<<(int*)p2.pname<<endl;
return 0;
}
3.解决这个问题的办法就是自己写拷贝构造函数,pname不再接收地址,而是重新创建一块内存存储数据,并指向这块新创建的内存地址。
#include <iostream>
#include <cstring>
using namespace std;
class person
{
public:
//有参构造函数,name是指针传递
person(char *name,int age)
{
pname = (char *)malloc(strlen(name)+1);
strcpy(pname,name);
mage = age;
cout<<"调用有参构造函数,pname地址:"<<(int*)pname<<" name地址:"<<(int*)name<<endl;
}
//拷贝构造函数,自己写
person(const person& p)
{
pname = (char*)malloc(strlen(p.pname)+1);
strcpy(pname,p.pname);
mage = p.mage;
cout<<"调用自己写的拷贝构造函数,pname地址:"<<(int*)pname<<" p.name地址:"<<(int*)p.pname<<endl;
}
//析构函数
~person()
{
cout<<"调用析构函数"<<endl;
//暂停,以便在程序崩溃之前看到打印信息
//cin.get();
if(NULL != pname)
{
free(pname);
pname = NULL;
}
}
char *pname;
int mage;
};
int main()
{
person p1("张三",66);
person p2(p1);
//通过打印可以看到一些信息
cout<<"p1.name="<<p1.pname<<" p1.mage="<<p1.mage<<" p1.name的地址:"<<(int*)p1.pname<<endl;
cout<<"p1.name="<<p1.pname<<" p1.mage="<<p1.mage<<" p2.name的地址:"<<(int*)p2.pname<<endl;
return 0;
}
总结:
当类中有指针,并且指针指向动态分配的内存空间
1.调用有参构造函数时,开辟来两块内存空间,存储一样的字符串,但是因为有动态申请内存,所以需手动写析构函数,默认析构函数不会释放堆区内存。
2.调用默认拷贝构造函数时,不新开辟内存空间,只是将源对象指向字符串的地址拷贝,也称浅拷贝。
3.解决第二个问题,需要自己写拷贝构造函数。新开辟一块内存存储数据,避免已经释放过的内存重复释放,造成程序中断。