• 构造函数、类成员、析构函数调用顺序


    1. 构造函数、类成员、析构函数调用顺序

    C++构造函数的调用次序是:

    基类的成员->基类的构造函数体->派生类的成员->派生类的构造函数体

    C++析构函数的调用次序是:

    派生类的析构函数->派生类的成员析构->基类的析构函数->基类的成员析构

    • 示例1:构造函数、类成员调用顺序
    • 示例2:基类、派生类构造函数(析构函数)调用顺序
    • 示例3:综合:基类、派生类构造函数(析构函数)、类成员调用顺序
    • 示例4:综合:基类、派生类虚析构函数(virtual虚函数)调用顺序

    1.1. 示例1:构造函数、类成员调用顺序

    #include <iostream>
     
    class ParentMember
    {
    public:
        ParentMember()
        {
            std::cout<<"ParentMember Construct"<<std::endl;
        }
     
        ~ParentMember()
        {
            std::cout<<"ParentMember Destruct"<<std::endl;
        }
    };
     
    
    class Parent
    {
    public:
        Parent()
        {
            std::cout<<"Parent Construct"<<std::endl;
        }
     
        ~Parent()
        {
            std::cout<<"Parent Destruct"<<std::endl;
        }
    private:
        ParentMember parentMember;
    };
     
     
    int main()
    {
        cout<<"Construct过程: "<<endl;
        Parent *p = new Parent;
        
        cout<<""<<endl;
    
        cout<<"Destruct过程: "<<endl;
        delete p;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    运行结果:

       Construct过程: 
       ParentMember Construct
       Parent Construct
    
       Destruct过程: 
       Parent Destruct
       ParentMember Destruct
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    从运行结果不难看出:
    1、在构造过程中,执行顺序为: 类成员->类的构造函数体。
    若断点调试,会发现,首先运行到构造函数的 '{'处,就会掉头先调用成员变量了。

    2、在析构过程中,执行顺序为:类的析构函数 -> 类的成员析构

    1.2. 示例2:基类、派生类构造函数(析构函数)调用顺序

    基类、派生类构造的顺序

    • 基(base) -> 派生(derived);即先构造基类, 再构造派生类;
    • 因为 基类 是独立于派生类的, 即不会调用派生类中的对象, 所以应该先被生成;
    • 如果派生类先于基类生成, 则因为无法调用基类资源, 可能生成失败;

    基类、派生类析构的顺序

    • 派生(derived) -> 基(base); 即先释放派生类, 再释放基类;
    • 因为 派生类 需要先释放调用的基类资源, 所以应该优先释放;
    • 如果基类先析构, 则有可能某些资源被派生类占用, 可能导致析构失败;
    #include <iostream>
    
    
    class Parent
    {
    public:
        Parent()
        {
             std::cout<<"Parent Construct"<< std::endl;
        }
     
        ~Parent()
        {
             std::cout<<"Parent Destruct"<< std::endl;
        }    
    };
    
    class Child: public Parent
    {
    public:
        Child()
        {
             std::cout<<"Child Construct"<< std::endl;
        }
        
        ~Child()
        {
             std::cout<<"Child Destruct"<< std::endl;
        }
    
    };
     
      
    int main()
    {
         std::cout<<"Construct过程: "<< std::endl;
         Parent *p = new Child;
        
         std::cout<<" "<< std::endl;
         
         std::cout<<"Destruct过程: "<< std::endl;
         delete p;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    运行结果:

       Construct过程: 
       Parent Construct
       Child Construct
       
       Destruct过程:
       Parent Destruct   
          
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    从运行结果不难看出:
    1、在构造过程中,执行顺序为: 先基类,后子类,即:基类的构造函数体 -> 子类的构造函数体
    若断点调试,会发现,首先运行到子类构造函数的 '{'处,就会掉头先调用基类成员变量了。

    2、在析构过程中,执行顺序为: 怎么仅基类呢?
    执行顺序本应该为:先子类,后基类,即子类的析构函数 -> 基类的析构函数。但实际运行效果我们不难发现,在最后释放指针p所指向的对象时,只释放了基类的部分,没有释放派生类的部分。这是因为,指针p声明时是基类的指针,而基类的析构函数不是虚函数,所以调用那个析构函数是在编译时确定的。如果要执行正确的析构顺序,需要将基类的析构函数定义为 virtual, 这样派生类的析构函数就自动是virtual的了,在最后释放指针p时,按照RTTI,执行p所指真实对象的析构函数。

    class Parent
    {
    public:
        Parent()
        {
             std::cout<<"Parent Construct"<< std::endl;
        }
     
        virtual ~Parent()   //增加virtual
        {
             std::cout<<"Parent Destruct"<< std::endl;
        }    
    };
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    修改后运行结果:

       Construct过程: 
       Parent Construct
       Child Construct
       
       Destruct过程:
       Child Destruct
       Parent Destruct   
          
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.3. 示例3:综合:基类、派生类构造函数(析构函数)、类成员调用顺序

    #include <iostream>
    
     
    class ParentMember
    {
    public:
        ParentMember()
        {
            std::cout<<"ParentMember Construct"<< std::endl;
        }
     
        ~ParentMember()
        {
             std::cout<<"ParentMember Destruct"<< std::endl;
        }
    };
     
    
    class Parent
    {
    public:
        Parent()
        {
             std::cout<<"Parent Construct"<< std::endl;
        }
     
        ~Parent()
        {
             std::cout<<"Parent Destruct"<< std::endl;
        }
        
    private:
        ParentMember parentMember;
        
    };
    
    class ChildMember
    {
    public:
        ChildMember()
        {
             std::cout<<"ChildMember Construct"<< std::endl;
        }
     
        ~ChildMember()
        {
            // std::cout<<"ChildMember Destruct"<< std::endl;
        }
    };
    
    class Child: public Parent
    {
    public:
        Child()
        {
             std::cout<<"Child Construct"<< std::endl;
        }
        
        ~Child()
        {
             std::cout<<"Child Destruct"<< std::endl;
        }
    private:
        ChildMember childMember;
    };
     
      
    int main()
    {
         std::cout<<"Construct过程: "<< std::endl;
         和Parent *p = new Child;
        
         std::cout<<""<< std::endl;
    
         std::cout<<"Destruct过程: "<< std::endl;
         delete p;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    运行结果:

       Construct过程: 
       ParentMember Construct
       Parent Construct
       ChildMember Construct
       Child Construct
       
       Destruct过程:
       Parent Destruct   
       ParentMember Destruct
          
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    从运行结果不难看出,在构造过程中,执行顺序为:

    • 先基类,后派生;
    • 基类先成员、后构造函数体;
    • 派生类先成员、后构造函数体;
    • 总体调用顺序为:基类的成员->基类的构造函数体->派生类的成员->派生类的构造函数体;
    • 若断点调试,会发现,首先运行到子类构造函数的 '{'处,就会掉头先调用基类成员变量了。

    析构函数执行顺序未按预期执行,详见:示例2尾部详细说明。修改方案,详见:示例4.

    1.4. 示例4:综合:基类、派生类虚析构函数(virtual虚函数)调用顺序 优化后方案

    
    class Parent
    {
    public:
        Parent()
        {
             std::cout<<"Parent Construct"<< std::endl;
        }
     
        virtual ~Parent()   //增加virtual
        {
             std::cout<<"Parent Destruct"<< std::endl;
        }
        
    private:
        ParentMember parentMember;
        
    };
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果:

       Construct过程: 
       ParentMember Construct
       Parent Construct
       ChildMember Construct
       Child Construct
       
       Destruct过程:
       Child Destruct
       //ChildMember Destruct  //未输出???
       Parent Destruct   
       ParentMember Destruct
          
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    不难看出以下规律:

    C++构造函数的调用次序是:

    基类的成员->基类的构造函数体->派生类的成员->派生类的构造函数体

    C++析构函数的调用次序是:

    派生类的析构函数->派生类的成员析构->基类的析构函数->基类的成员析构

  • 相关阅读:
    [ubuntu]安装robo 3t
    Linux系统中查看当前文件夹下文件的个数
    Linux知识汇总
    软件工程综合实践课程第二周作业(简单工厂模式实验)
    面试官:一个 SpringBoot 项目能处理多少请求?(小心有坑)
    时态图根据时间轴动态播放热力图
    rabbitmq入门
    为什么在Kubernetes上运行虚拟机?
    语言模型和词向量的发展史及其原理
    【Java】运算符
  • 原文地址:https://blog.csdn.net/limeigui/article/details/125528721