zoukankan      html  css  js  c++  java
  • c++类的构造析构顺序

    1 前言

    程序的正确运行依赖于对变量生命周期的管理,但是类的构造和析构顺序有时非常隐蔽,控制不好可能会引发不可预知的错误,所以本文探讨一下c++类的构造和析构顺序。

    2 构造析构顺序的影响因素

    构造析构顺序主要受类与类之间的关系和类的作用域的影响。

    • 类与类之间的关系
      • 继承关系
        • 单继承
        • 多继承
        • 菱形继承
      • 包含关系/成员变量,比如A是B的成员变量
      • 声明的先后顺序
    • 类的作用域
      • 全局变量
      • 静态变量
      • 局部变量

    结合以上因素,通过设计实验来验证构造析构的顺序,回答以下几个问题:

    • 父类和子类的构造析构顺序
    • 父类和成员变量的声明顺序对构造析构顺序的影响
    • 作用域与构造析构顺序的关系

    3 实验

    3.1 实验设计

    /**
     * 测试构造函数和析构函数的顺序,包含以下6种情况
     *  1. 单继承
     *  2. 多继承
     *  3. 菱形继承
     *  4. 包含成员变量
     *  5. 声明顺序和构造/析构顺序
     *  6. 既有继承又有成员变量
     * */
    
    #include <iostream>
    #include <memory>
    class Base {
    public:
      Base() {
        std::cout << "Base: constructor" << std::endl;
      }
      virtual ~Base() {
        std::cout << "~Base: destructor" << std::endl;
      }
    };
    
    class A : public Base {
    public:
      A() {
        std::cout << "A: constructor" << std::endl;
      }
      virtual ~A() {
        std::cout << "~A: destructor" << std::endl;
      }
    };
    
    class B : public Base {
    public:
      B() {
        std::cout << "B: constructor" << std::endl;
      }
      virtual ~B() {
        std::cout << "~B: destructor" << std::endl;
      }
    };
    
    class AB : public A, public B {
    public:
      AB() {
        std::cout << "AB: constructor" << std::endl;
      }
      virtual ~AB() {
        std::cout << "~AB: destructor" << std::endl;
      }
    };
    
    class BA : public B, public A {
    public:
      BA() {
        std::cout << "BA: constructor" << std::endl;
      }
      virtual ~BA() {
        std::cout << "~BA: destructor" << std::endl;
      }
    };
    
    class MemberA {
    public:
      MemberA() {
        std::cout << "MemberA: constructor" << std::endl;
      }
      virtual ~MemberA() {
        std::cout << "~MemberA: destructor" << std::endl;
      }
    };
    
    class MemberB {
    public:
      MemberB() {
        std::cout << "MemberB: constructor" << std::endl;
      }
      virtual ~MemberB() {
        std::cout << "~MemberB: destructor" << std::endl;
      }
    };
    
    class HasMemberAB {
    public:
      HasMemberAB()
        : mb_(new MemberB()),
          ma_(new MemberA()) {
        std::cout << "HasMemberAB: constructor" << std::endl;
      }
      virtual ~HasMemberAB() {
        std::cout << "~HasMemberAB: destructor" << std::endl;
      }
    private:
      std::shared_ptr<MemberA> ma_;
      std::shared_ptr<MemberB> mb_;
    };
    
    class HasMemberBA {
    public:
      HasMemberBA()
        : ma_(new MemberA()),
          mb_(new MemberB()) {
        std::cout << "HasMemberBA: constructor" << std::endl;
      }
      virtual ~HasMemberBA() {
        std::cout << "~HasMemberBA: destructor" << std::endl;
      }
    private:
      std::shared_ptr<MemberB> mb_;
      std::shared_ptr<MemberA> ma_;
    };
    
    class DerivedAndMember : public A, public B {
    public:
      DerivedAndMember() {
        std::cout << "DerivedAndMember: constructor" << std::endl;
      }
      virtual ~DerivedAndMember() {
        std::cout << "~DerivedAndMember: destructor" << std::endl;
      }
    private:
      MemberA ma_;
      MemberB mb_;
    };
    
    void Test01_SingleDerived() {
      std::cout << "测试01:单继承 {" << std::endl;
      {
        A a;
      }
      std::cout << "}
    
    " << std::endl;
    }
    
    void Test02_MultiDerived() {
      std::cout << "测试02:多继承 {" << std::endl;
      {
        std::cout << "case 1: 先继承A,再继承B" << std::endl;
        AB ab;
      }
      {
        std::cout << "case 2: 先继承B,再继承A" << std::endl;
        BA ba;
      }
      std::cout << "}
    
    " << std::endl;
    }
    
    void Test03_HasMember() {
      std::cout << "测试03:包含成员变量 {" << std::endl;
      {
        std::cout << "case 1: 先声明A,再声明B" << std::endl;
        HasMemberAB hasMemberAB;
      }
      {
        std::cout << "case 2: 先声明B,再声明A" << std::endl;
        HasMemberBA hasMemberBA;
      }
      std::cout << "}
    
    " << std::endl;
    }
    
    void Test04_DerivedAndMember() {
      std::cout << "测试04:既有多继承,又有成员变量 {" << std::endl;
      {
        DerivedAndMember dm;
      }
      std::cout << "}
    
    " << std::endl;
    }
    
    int main() {
      Test01_SingleDerived();
      Test02_MultiDerived();
      Test03_HasMember();
      Test04_DerivedAndMember();
      return 0;
    }
    

    3.2 实验结果

    // output
    测试01:单继承 {
    Base: constructor
    A: constructor
    ~A: destructor
    ~Base: destructor
    }
    /** 
     * 结论:
     *  单继承情况下,
     *  - 构造:先构造父类,再构造子类
     *  - 析构:先析构子类,再析构父类
     */
    
    // output
    测试02:多继承 {
    case 1: 先继承A,再继承B
    Base: constructor
    A: constructor
    Base: constructor
    B: constructor
    AB: constructor
    ~AB: destructor
    ~B: destructor
    ~Base: destructor
    ~A: destructor
    ~Base: destructor
    case 2: 先继承B,再继承A
    Base: constructor
    B: constructor
    Base: constructor
    A: constructor
    BA: constructor
    ~BA: destructor
    ~A: destructor
    ~Base: destructor
    ~B: destructor
    ~Base: destructor
    }
    /**
     * 结论:
     *  多继承情况下,
     *  - 构造:父类的构造顺序和声明顺序一致(先声明先构造)
     *  - 析构:父类的析构顺序和声明顺序相反(先声明后析构,或者说先构造的后析构)
     */
    
    // output
    测试03:包含成员变量 {
    case 1: 先声明A,再声明B
    MemberA: constructor
    MemberB: constructor
    HasMemberAB: constructor
    ~HasMemberAB: destructor
    ~MemberB: destructor
    ~MemberA: destructor
    case 2: 先声明B,再声明A
    MemberB: constructor
    MemberA: constructor
    HasMemberBA: constructor
    ~HasMemberBA: destructor
    ~MemberA: destructor
    ~MemberB: destructor
    }
    /**
     * 结论:
     *  类中包含成员的情况下
     * - 构造:先构造成员变量,再构造自身
     *        类中成员变量的构造顺序是:先声明先构造,和构造函数中的初始化列表的顺序无关
     * - 析构:先运行类的析构函数,在析构成员,成员的析构顺序是:先声明后析构;
     */
    
    // output
    测试04:既有多继承,又有成员变量 {
    Base: constructor
    A: constructor
    Base: constructor
    B: constructor
    MemberA: constructor
    MemberB: constructor
    DerivedAndMember: constructor
    ~DerivedAndMember: destructor
    ~MemberB: destructor
    ~MemberA: destructor
    ~B: destructor
    ~Base: destructor
    ~A: destructor
    ~Base: destructor
    }
    }
    /**
     * 结论:
     *  既有继承,又有成员变量的情况下
     *  - 构造:先构造父类,再构造成员类,最后构造自身(子类)
     *  - 析构:先析构本身(子类),再析构成员,最后再析构父类
     */
    

    4 结论

    通过以上4个实验,可以得出最终结论:

    1. 构造顺序
      1. a. 先父类,再成员变量(类),最后自身(父类->成员类->自身
      2. 在多继承多成员变量的情况下,都是先声明先构造
    2. 析构顺序
      1. 先自身,再成员变量,最后父类(自身->成员类->父类)
      2. 在多继承多成员的情况下,都是先声明后析构
      3. 或者说,析构顺序和构造顺序相反,先构造的后析构
    3. 作用域
      1. 在不考虑引用计数的情况下,类出了作用域就会被析构
  • 相关阅读:
    PyQt作品 – PingTester – 多点Ping测试工具
    关于和技术人员交流的一二三
    Pyjamas Python Javascript Compiler, Desktop Widget Set and RIA Web Framework
    Hybrid Qt applications with PySide and Django
    pyjamas build AJAX apps in Python (like Google did for Java)
    PyQt 维基百科,自由的百科全书
    InfoQ:请问为什么仍要选择Java来处理后端的工作?
    Eric+PyQt打造完美的Python集成开发环境
    python select module select method introduce
    GUI Programming with Python: QT Edition
  • 原文地址:https://www.cnblogs.com/HongyunL/p/13976947.html
Copyright © 2011-2022 走看看