zoukankan      html  css  js  c++  java
  • C++构造函数/析构函数/拷贝构造函数/深拷贝浅拷贝解析

    参考文献:C++中构造函数与析构函数的调用顺序

    1.形参与实参

    形参:是函数声明时的参数,只说明参数名和类型,不是实际的参数,不能真正使用。 
    实参:运行时传给函数的参数,是实际的变量,形参在这时真正被分配空间,并复制了实参的值。

    一个函数的实参在内存中有自己固定的内存,直到函数执行结束才释放内存。 形参没有固定的内存,只在调用函数的时候有一个虚拟内存,等调用完毕就不再有内存。。他们的关系是在函数调用的时候,实参把值传给形参。 

    2.构造函数

    1. 构造函数不能有返回值,函数名为类名。
    2. 缺省构造函数时,系统将自动调用该缺省构造函数初始化对象,缺省构造函数会将所有数据成员都初始化为零或空。缺省构造函数是不带参数的。
    3. 创建一个对象时,系统自动调用构造函数。

    3.析构函数

    析构函数没有参数,也没有返回值。不能重载,也就是说,一个类中只可能定义一个析构函数。如果一个类中没有定义析构函数,系统也会自动生成一个默认的析构函数,为空函数,什么都不做。调用条件:

    1. 在函数体内定义的对象,当函数执行结束时,该对象所在类的析构函数会被自动调用;(在一个函数中定义一个对象,当函数调用结束,则会自动调用析构函数来删除在这个函数体内创建的对象。包括主函数。)
    2. 用new运算符动态构建的对象,在使用delete运算符释放它时。

    4.拷贝构造函数

    拷贝构造函数实际上也是构造函数,具有一般构造函数的所有特性,其名字也与所属类名相同。拷贝构造函数中只有一个参数,这个参数是对某个同类对象的引用。
    在三种情况下被调用:可参考:http://blog.csdn.net/bluescorpio/article/details/4322682

    1. 用类的一个已知的对象去初始化该类的另一个对象时。(初始化时用"A=B",也可以用A(B)的形式。)
    2. 函数的形参是类的对象,调用函数进行形参和实参的结合时。(定义一个函数A,A函数的形参是类的对象,在另外一个函数B中调用这个函数A,实参将具体的对象传递给形参,这时候会调用拷贝构造函数。)
    3. 函数的返回值是类的对象,函数执行完返回调用者。(定义一个函数A,该函数的返回值是一个类的对象,在函数B中定义类的对象来接受函数A的返回,这个时候会调用拷贝构造函数。)

    5.深拷贝与浅拷贝

    浅拷贝

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

    View Code
    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.prect2.p具有相同的值,也即这两个指针指向了堆里的同一个空间,如下图所示:

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

    深拷贝

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

    View Code
    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各自指向一段内存空间,但它们指向的空间具有相同的内容,这就是所谓的“深拷贝”。

    总结:

    拷贝构造函数中分为深拷贝和浅拷贝,一般情况下浅拷贝已经满足需求,但是当存在动态成员时,浅拷贝就不能满足需求了。比如一个对象中有指针成员,只是通过简单的浅拷贝,只能够让复制的对象指向同一片区域,而不是创建一片同样大小的区域。这就需要通过深拷贝来解决。

  • 相关阅读:
    iOS resign code with App Store profile and post to AppStore
    HTTPS科普扫盲帖 对称加密 非对称加密
    appid 评价
    使用Carthage安装第三方Swift库
    AngularJS:何时应该使用Directive、Controller、Service?
    xcode7 The operation couldn't be completed.
    cocoapods pod install 安装报错 is not used in any concrete target
    xcode7 NSAppTransportSecurity
    learning uboot how to set ddr parameter in qca4531 cpu
    learning uboot enable protect console
  • 原文地址:https://www.cnblogs.com/xwdreamer/p/2428016.html
Copyright © 2011-2022 走看看