“浅拷贝”与“深拷贝”

来源:互联网 发布:淘宝摩托车准备店铺 编辑:程序博客网 时间:2024/05/17 09:19
浅拷贝

       所谓浅拷贝,指的是在对象复制时,只是对对象中的数据成员进行简单的赋值,上面的例子都是属于浅拷贝的情况,默认拷贝构造函数执行的也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就会出问题了,让我们考虑如下一段代码:

    class Rect      {      public:          Rect()      // 构造函数,p指向堆中分配的一空间          {              p = new int(100);          }          ~Rect()     // 析构函数,释放动态分配的空间          {              if(p != NULL)                 {                  delete p;              }          }      private:          int width;          int height;          int *p;     // 一指针成员      };            int main()      {          Rect rect1;          Rect rect2(rect1);   // 复制对象          return 0;      }  
    在这段代码运行结束之前,会出现一个运行错误。原因就在于在进行对象复制时,对于动态分配的内容没有进行正确的操作。我们来分析一下:

       在运行定义rect1对象后,由于在构造函数中有一个动态分配的语句,    在使用rect1复制rect2时,由于执行的是浅拷贝,只是将成员的值进行赋值,所以此时rect1.p和rect2.p具有相同的值,也即这两个指针指向了堆里的同一个空间,

  当然,这不是我们所期望的结果,在销毁对象时,两个对象的析构函数将对同一个内存空间释放两次,这就是错误出现的原因。我们需要的不是两个p有相同的值,而是两个p指向的空间有相同的值,解决办法就是使用“深拷贝”。

深拷贝

       在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间,如上面的例子就应该按照如下的方式进行处理:

    class Rect      {      public:          Rect()      // 构造函数,p指向堆中分配的一空间          {              p = new int(100);          }          Rect(const Rect& r)          {              width = r.width;              height = r.height;              p = new int;    // 为新对象重新动态分配空间              *p = *(r.p);          }          ~Rect()     // 析构函数,释放动态分配的空间          {              if(p != NULL)                 {                  delete p;              }          }      private:          int width;          int height;          int *p;     // 一指针成员      };  

此时rect1的p和rect2的p各自指向一段内存空间,但它们指向的空间具有相同的内容,这就是所谓的“深拷贝”。

       此外,在与“对象的复制”很类似的“对象的赋值”的情况下,也会出现同样的问题。在“对象的赋值”一文中再来讨论此问题。

       通过对对象复制的分析,我们发现对象的复制大多在进行“值传递”时发生,这里有一个小技巧可以防止按值传递——声明一个私有拷贝构造函数。甚至不必去定义这个拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类对象,将得到一个编译错误,从而可以避免按值传递或返回对象。

 一些需要注意的东西

       (1):String字符串对象是引用对象,但是很特殊,它表现的如值对象一样,即对它进行赋值,分割,合并,并不是对原有的字符串进行操作,而是返回一个新的字符串对象

       (2):Array数组对象是引用对象,在进行赋值的时候,实际上返回的是源对象的另一份引用而已;因此如果要对数组对象进行真正的复制(深拷贝),那么需要新建一份数组对象,然后将源数组的值逐一拷贝到目的对象中

本文转自

http://blog.csdn.net/bluescorpio/article/details/4322682


0 0