关于深层拷贝的深入讲解
以下引用的代码都极其好懂,但又把重点深层拷贝的机理体现了出来深层拷贝的例子:
例子1
#include <iostream>
using namespace std;
class CClass
{
public:
CClass(char *cName,int snum);
CClass(CClass &p)
{
pname=new char[strlen(p.pname)+1];/*此语句为成员变量(指针变量)分配空间,避免两个后面调用CClass c2(c1);使c2和c1都指向同一内存空间,造成栈中对象的生命结束时调用析构函数对同一堆资源delete两次出现错误。所以要给pname重新分配新的空间,这样在对象生命结束时,析构函数只对自己的那块堆资源delete,而不会产生对同一堆资源delete两次*/
if(pname!=0)
strcpy(pname,p.pname);
num=p.num;/*num不是指针变量按值复制不会出错,因为两个都有自己的空间,这不同于指针,指针是指向某个地址的。按值复制会使两个指针都指向同一地址即共享同一内存空间而造成致命错误*/
cout<<"创建班级的拷贝:"<<pname<<endl;
}
/*若不自己定义系统默认的浅层复制必然是这样的
CClass(CClass &p)
{
pname=p.name;pname对比深层拷贝pname没有开辟自己的堆中空间
num=p.num
}*/
~CClass()
{
cout<<"析构班级:"<<pname<<endl;
delete []pname;
}
void Print();
private:
char *pname;
int num;
};
CClass::CClass(char *cName,int snum)
{
int length=strlen(cName);
pname=new char[length+1];
if(pname!=0)
strcpy(pname,cName);
num=snum;
cout<<"创建班级:"<<pname<<endl;
}
void CClass::Print()
{
cout<<pname<<"班的人数为:"<<num<<endl;
}
void main()
{
CClass c1("计算机061班",56);
CClass c2(c1);
c1.Print();
c2.Print();
}
以上为字符指针,如果为其它类的指针时同理,见如下例子:
例子2
#include <iostream>
using namespace std;
class A
{
public:
A(){x=new int;*x=5;}
~A(){delete x;x=NULL;}
A(const A&a)
{
cout<<"复制构造函数执行...\n"<<endl;
x=new int;/*开辟新的存储单元让成员变量x指向它,这样就避免了上面的共用同一存储空间发生错误,对比x=a.x,它没有开辟新的存储单元,而是将a.x指向的单元赋予给了它,而执行x=new int 后,就开辟的新的存储单元*/
*x=*(a.x);//将a.x指向的值复制给this指针的x成员,实现值的复制
}
int print()const{return *x;}
void set(int i){*x=i;}
private:
int *x;
};
int main()
{
A*a=new A();
cout<<"a:"<<a->print()<<endl;
A b=(*a);
cout<<"a:"<<a->print()<<endl;
cout<<"b:"<<b.print()<<endl;
delete a;
return 0;
}
以上两个程序段的代码必须使用深层拷贝,使用浅层拷贝就等价于下面这种情况:
例子3
#include <iostream>
using namespace std;
void main()
{
int *p=new int;
delete p;
delete p;
}
这样会导致对该指针p的再次删除导致崩溃(并不非要是同一指针,它的实质是:对同一内存单元释放两次会导致程序崩溃,这在浅层复制指针变量时很容易出现)简单而言,1个new对应一个delete才是安全的
要想例子3不崩溃必须将指针赋值为空
#include <iostream>
using namespace std;
void main()
{
int *p=new int;
delete p;
p=NULL;
delete p;//删除空指针不会崩溃
}
有人说例子2的类A的析构函数中,x被赋值为NULL,为什么也崩溃了
~A(){delete x;x=NULL;}
这是因为第一次调用析构函数时执行了代码delete x;x=NULL;( delete a;时执行的代码段)
第二次栈中对象b的生存期结束时又执行了delete x; x=NULL;(遇到}对象b生命结束)。若两次都是同一变量x则不会崩溃,但可惜的是第二次的那个x是对象b的成员变量x,不是上次那个堆中对象a的成员变量x,在堆中对象消失时成员变量x也随即消失了,所以同一堆资源仍然被delete两次,程序崩溃。这就是所谓的对同一内存单元释放两次会导致程序崩溃。
现在你应该能灵活运用了:
例子4(修改例子2,不使用深层复制但使程序不崩溃)
#include <iostream>
using namespace std;
class A
{
public:
A(){x=new int;*x=5;}
~A(){delete x;x=NULL;}
/*A(const A&a)
{
cout<<"复制构造函数执行...\n"<<endl;
x=new int;
*x=*(a.x);
}*/
int print()const{return *x;}
void set(int i){*x=i;}
private:
int *x;
};
int main()
{
A*a=new A();
cout<<"a:"<<a->print()<<endl;
A b=(*a);
cout<<"a:"<<a->print()<<endl;
cout<<"b:"<<b.print()<<endl;
//delete a;若将词句注释起来,即使不采用深层复制也不会崩溃
return 0;
}