浅拷贝,深拷贝

来源:互联网 发布:html引用javascript 编辑:程序博客网 时间:2024/04/29 11:17

对于普通类型的对象来说,他们之间的复制是非常简单的,例如:

  

自己定义的类的对象同样是对象,谁也不能阻止我们用以下的方式进行复制,例如:

 

普通对象和类对象同为对象,他们之间的特性有相似之处也有不同之处,类对象内部存在成员变量,而普通对象是没有的,当同样的复制方法发生在不同的对象上的时候,那么系统对他们进行的操作也是不相同的,就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的,在上面的代码中,我们并没有看到拷贝构造函数,同样完成了复制工作,这又是为什么呢?因为当一个类没有自定义的拷贝构造函数的时候系统会自动提供一个默认的拷贝构造函数,来完成复制工作。

下面,我们为了说明情况,就普通情况而言(以上面的代码为例),我们来自己定义一个和系统默认拷贝构造函数相同的拷贝构造函数,看看他的内部是怎么工作的!

 

代码如下:

 

上面代码中的Test(Test &c_t)就是我们自定义的拷贝构造函数,拷贝构造函数的名称必须和类名称一致,函数的形式参数是本类型的一个引用变量,且必须是引用。

当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用,如果你没有自定义拷贝构造函数的时候系统将会提供给一个默认的拷贝构造函数来完成这个过程,上面代码的复制核心语句就是通过Test(Test &c_t)拷贝构造函数内的p1=c_t.p1;语句完成的。如果取掉这句代码,那么b对象的p1属性将得到一个未知的随机值;

下面我们来讨论一下关于浅拷贝和深拷贝的问题。

就上面的代码情况而言,非常多人会问到,既然系统会自动提供一个默认的拷贝构造函数来处理复制,那么我们没有意义要去自定义拷贝构造函数呀,对,就普通情况而言这的确是没有必要的,但在某写状况下,类体内的成员是需要开辟动态开辟堆内存的,如果我们不自定义拷贝构造函数而让系统自己处理,那么就会导致堆内存的所属权产生混乱,试想一下,已开辟的一端堆地址原来是属于对象a的,由于复制过程发生,b对象取得是a已开辟的堆地址,一旦程式产生析构,释放堆的时候,计算机是不可能清晰这段地址是真正属于谁的,当连续发生两次析构的时候就出现了运行错误。

为了更周详的说明问题,请看如下的代码。

 

客户端输出:

载入构造函数

载入COPY构造函数

载入COPY构造函数

载入test函数

载入析构函数

 

上面代码就演示了深拷贝的问题,对对象b的cname属性采取了新开辟内存的方式避免了内存归属不清所导致析构释放空间时候的错误,最后我必须提一下,对于上面的程式我的解释并不多,就是希望读者本身运行程式观察变化,进而深刻理解。

深拷贝和浅拷贝的定义能简单理解成:如果一个类拥有资源(堆,或是其他系统资源),当这个类的对象发生复制过程的时候,这个过程就能叫做深拷贝,反之对象存在资源但复制过程并未复制资源的情况视为浅拷贝。

浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程式运行出错,这点尤其需要注意!

以前我们的教程中讨论过函数返回对象产生临时变量的问题,接下来我们来看一下在函数中返回自定义类型对象是否也遵循此规则产生临时对象!

先运行下列代码:

 

客户端输出:

载入构造函数

载入COPY构造函数

载入析构函数

载入析构函数

载入析构函数

 

从上面的代码运行结果能看出,程式一共载入过析构函数三次,证实了由函数返回自定义类型对象同样会产生临时变量,事实上对象a得到的就是这个临时Internet类类型对象temp的值。

这一下节的内容我们来说一下无名对象。

利用无名对象初始化对象系统不会不调用拷贝构造函数。

那么什么又是无名对象呢?

非常简单,如果在上面程式的main函数中有:

 

Internet ("test","www.test.com");

 

这样的一句语句就会产生一个无名对象,无名对象会调用构造函数但利用无名对象初始化对象系统不会不调用拷贝构造函数!

下面三段代码是非常见到的三种利用无名对象初始化对象的例子。

 

客户端输出:

载入构造函数

载入析构函数

 

上面代码的运行结果有点“出人意料”,从思维逻辑上说,当无名对象创建了后,是应该调用自定义拷贝构造函数,或是默认拷贝构造函数来完成复制过程的,但事实上系统并没有这么做,因为无名对象使用过后在整个程式中就失去了作用,对于这种情况c++会把代码看成是:

 

Internet a("test","www.test.com");

 

省略了创建无名对象这一过程,所以说不会调用拷贝构造函数。

最后让我们来看看引用无名对象的情况。

 

客户端输出:

载入构造函数

载入析构函数

 

引用本身是对象的别名,和复制并没有关系,所以不会调用拷贝构造函数,但要注意的是,在c++看来:

 

Internet &a=Internet("test","www.test.com")是和Internet a("test","www.test.com")等价的

 

,注意观察调用析构函数的位置(这种情况是在main()外调用,而无名对象本身是在main()内析构的)。

 

源文档 <http://www.sudu.cn/info/html/edu/20080403/260784.html>

 

原创粉丝点击