下面我们来讨论一下关于浅拷贝和深拷贝的问题。
就上面的代码情况而言,很多人会问到,既然系统会自动提供一个默认的拷贝构造函数来处理复制,那么我们没有意义要去自定义拷贝构造函数呀,对,就普通情况而言这的确是没有必要的,但在某写状况下,类体内的成员是需要开辟动态开辟堆内存的,如果我们不自定义拷贝构造函数而让系统自己处理,那么就会导致堆内存的所属权产生混乱,试想一下,已经开辟的一端堆地址原来是属于对象a的,由于复制过程发生,b对象取得是a已经开辟的堆地址,一旦程序产生析构,释放堆的时候,计算机是不可能清楚这段地址是真正属于谁的,当连续发生两次析构的时候就出现了运行错误。
为了更详细的说明问题,请看如下的代码。
#include <iostream> using namespace std; class Internet { public: Internet(char *name,char *address) { cout<<"载入构造函数"<<endl; strcpy(Internet::name,name); strcpy(Internet::address,address); cname=new char[strlen(name)+1]; if(cname!=NULL) { strcpy(Internet::cname,name); } } Internet(Internet &temp) { cout<<"载入COPY构造函数"<<endl; strcpy(Internet::name,temp.name); strcpy(Internet::address,temp.address); cname=new char[strlen(name)+1];//这里注意,深拷贝的体现! if(cname!=NULL) { strcpy(Internet::cname,name); } } ~Internet() { cout<<"载入析构函数!"; delete[] cname; cin.get(); } void show(); protected: char name[20]; char address[30]; char *cname; }; void Internet::show() { cout<<name<<":"<<address<<cname<<endl; } void test(Internet ts) { cout<<"载入test函数"<<endl; } void main() { Internet a("中国软件开发实验室","www.cndev-lab.com"); Internet b = a; b.show(); test(b); }
上面代码就演示了深拷贝的问题,对对象b的cname属性采取了新开辟内存的方式避免了内存归属不清所导致析构释放空间时候的错误,最后我必须提一下,对于上面的程序我的解释并不多,就是希望读者本身运行程序观察变化,进而深刻理解。
拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,这个过程就可以叫做深拷贝,反之对象存在资源但复制过程并未复制资源的情况视为浅拷贝。
浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错,这点尤其需要注意!
以前我们的教程中讨论过函数返回对象产生临时变量的问题,接下来我们来看一下在函数中返回自定义类型对象是否也遵循此规则产生临时对象!
|