zoukankan      html  css  js  c++  java
  • 拷贝构造函数(二)——深拷贝与浅拷贝

    拷贝构造函数(一)——哲学三连:http://www.cnblogs.com/tenjl-exv/p/8017814.html

    拷贝构造函数(二)——深拷贝与浅拷贝:http://www.cnblogs.com/tenjl-exv/p/8017909.html

    拷贝构造函数(三)——重载赋值运算符:http://www.cnblogs.com/tenjl-exv/p/8017983.html

    1. 默认拷贝构造函数

    很多时候在我们都不知道拷贝构造函数的情况下,

    传递对象给函数参数或者函数返回对象都能很好的进行,

    这是因为编译器会给我们自动产生一个拷贝构造函数,

    这就是“默认拷贝构造函数”。

    这个构造函数很简单,仅仅使用“老对象”的数据成员的值对“新对象”的数据成员一一进行赋值,

    它一般具有以下形式:

    1 Rect::Rect(const Rect& r)  
    2 {  
    3     width = r.width;  
    4     height = r.height;  
    5 }  

    当然,以上代码不用我们编写,编译器会为我们自动生成。

    但是如果认为这样就可以解决对象的复制问题,那就错了。

    2. 浅拷贝

    所谓浅拷贝,指的是在对象复制时,

    只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。

    大多情况下“浅拷贝”已经能很好地工作了,

    但是一旦对象存在了动态成员,那么浅拷贝就会出问题了。

    让我们考虑如下一段代码:

     1 class Rect  
     2 {  
     3 public:  
     4     Rect()      // 构造函数,p指向堆中分配的一空间  
     5     {  
     6         p = new int(100);  
     7     }  
     8     ~Rect()     // 析构函数,释放动态分配的空间  
     9     {  
    10         if(p != NULL)  
    11         {  
    12             delete p;  
    13         }  
    14     }  
    15 private:  
    16     int width;  
    17     int height;  
    18     int *p;     // 一指针成员  
    19 };  
    20   
    21 int main()  
    22 {  
    23     Rect rect1;  
    24     Rect rect2(rect1);   // 复制对象  
    25     return 0;  
    26 }  

    在这段代码运行结束之前,会出现一个运行错误。

    原因就在于在进行对象复制时,对于动态分配的内容没有进行正确的操作。

    我们来分析一下:

    在运行定义rect1对象后,

    由于在构造函数中有一个动态分配的语句,因此执行后的内存情况大致如下:

     

     

    在使用rect1复制rect2时,由于执行的是浅拷贝,

    只是将成员的值进行赋值,这时 rect1.p = rect2.p,

    也即这两个指针指向了堆里的同一个空间,如下图所示:

    当然,这不是我们所期望的结果,

    在销毁对象时,两个对象的析构函数将对同一个内存空间释放两次,这就是错误出现的原因。

    我们需要的不是两个p有相同的值,而是两个p指向的空间有相同的值,解决办法就是使用“深拷贝”。

    3. 深拷贝

    在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,

    而应该重新动态分配空间,如上面的例子就应该按照如下的方式进行处理:

     1 class Rect  
     2 {  
     3     public:  
     4         Rect()      // 构造函数,p指向堆中分配的一空间  
     5         {  
     6             p = new int(100);  
     7         }  
     8         Rect(const Rect& r)  
     9         {  
    10             width = r.width;  
    11             height = r.height;  
    12             p = new int;        // 为新对象重新动态分配空间  
    13             *p = *(r.p);  
    14         }  
    15         ~Rect()                  // 析构函数,释放动态分配的空间  
    16         {  
    17             if(p != NULL)  
    18             {  
    19                 delete p;  
    20             }  
    21         }  
    22     private:  
    23         int width;  
    24         int height;  
    25         int *p;     // 一指针成员  
    26 };  

    此时,在完成对象的复制后,内存的一个大致情况如下:

     

    此时rect1的p和rect2的p各自指向一段内存空间,

    但它们指向的空间具有相同的内容,这就是所谓的“深拷贝”。

    四. 拷贝构造函数的几个细节

    1. 拷贝构造函数里能调用private成员变量吗?

    这个问题是在网上见的,

    其时从名字我们就知道拷贝构造函数其时就是一个特殊的构造函数,

    操作的还是自己类的成员变量,所以不受private的限制。

    2. 以下函数哪个是拷贝构造函数,为什么?

    X::X(const X&);      
    X::X(X);      
    X::X(X&, int a=1);      
    X::X(X&, int a=1, int b=2);  

    对于一个类X, 如果一个构造函数的第一个参数是下列之一:

    a) X&
    b) const X&
    c) volatile X&
    d) const volatile X&

    且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.

    X::X(const X&);                 //是拷贝构造函数      
    X::X(X&, int=1);                //是拷贝构造函数     
    X::X(X&, int a=1, int b=2);     //当然也是拷贝构造函数  

    3. 一个类中可以存在多于一个的拷贝构造函数吗?
    类中可以存在超过一个拷贝构造函数。

    class X {   
    public:         
      X(const X&);      // const 的拷贝构造  
      X(X&);            // 非const的拷贝构造  
    };  
     

    注意,如果一个类中只存在一个参数为 X& 的拷贝构造函数,

    那么就不能使用const X或volatile X的对象实行拷贝初始化.

    class X {      
    public:  
      X();      
      X(X&);  
    };      
      
    const X cx;      
    X x = cx;    // error  

    如果一个类中没有定义拷贝构造函数,

    那么编译器会自动产生一个默认的拷贝构造函数。
    这个默认的参数可能为 X::X(const X&)或 X::X(X&),

    由编译器根据上下文决定选择哪一个。

     

  • 相关阅读:
    玩4K必备知识:HDMI1.4、2.0、2.0a、2.0b接口参数对比【扫盲贴】
    Gradle配置最佳实践
    Android Studio 中安装 apk 被拆分成多个 slice,如何禁止?
    编译不同平台 设定
    编译找错
    Delphi 中内存映射对于大文件的使用
    linux——nmap端口扫描命令
    Android 使用 adb命令 远程安装apk
    插件编译 版本问题
    2019 opensuse linux Eclipse
  • 原文地址:https://www.cnblogs.com/tenjl-exv/p/8017909.html
Copyright © 2011-2022 走看看