在C ++中调用析构函数和构造函数的顺序是什么

时间:2009-03-17 14:12:08

标签: c++ constructor destructor

在C ++中调用析构函数和构造函数的顺序是什么?使用一些基类和派生类的示例

5 个答案:

答案 0 :(得分:58)

订单是:

  1. 基础构造函数
  2. 派生构造函数
  3. 派生的析构函数
  4. 基础析构函数
  5. 示例:

    class B
    {
    public:
      B()
      {  
        cout<<"Construct B"<<endl;
      }
    
      virtual ~B()
      {
        cout<<"Destruct B"<<endl;
      }
    };
    
    class D : public B
    {
    public:
      D()
      {  
        cout<<"Construct D"<<endl;
      }
    
      virtual ~D()
      {
        cout<<"Destruct D"<<endl;
      }
    };
    
    
    
    int main(int argc, char **argv)
    {
      D d; 
      return 0;
    }
    

    示例输出:

      

    构建B

         

    构造D

         

    破坏D

         

    破坏B

    多层继承的工作方式类似于堆栈:

    如果考虑将一个项目作为构造推送到堆栈中,并将其作为破坏程序将其删除,那么您可以像堆栈一样查看多个级别的继承。

    这适用于任何级别。

    示例D2派生自D,源自B.

    在堆栈上按B,在堆栈上按D,在堆栈上按D2。所以施工顺序是B,D,D2。然后找出销毁订单开始弹出。 D2,D,B

    更复杂的例子:

    有关更复杂的示例,请参阅@JaredPar提供的链接

答案 1 :(得分:21)

C ++ FAQ Lite提供了有关这些事件的详细说明,包括虚拟和多重继承。第25.14和25.15节

https://isocpp.org/wiki/faq/multiple-inheritance#mi-vi-ctor-order

答案 2 :(得分:10)

另外,请记住,首先构造数组元素 - &gt;最后,它们以相反的顺序被破坏:last - &gt;第一

答案 3 :(得分:4)

我必须添加到之前的答案,因为每个人似乎都在忽略它

如果派生类实例已创建,则 中的代码构造函数确实如此 之前 ,但请记住派生基础之前仍然在技术上“创建”

当您调用派生的类析构函数时,派生析构函数中的代码 确实称为之前 基础析构函数之前,还要记住基础 已销毁 之前 派生

当我说创建/销毁时,我实际上指的是已分配/已取消分配

如果查看这些实例的内存布局,您将看到派生实例组成基本实例。例如:

派生内存:0x00001110至0x00001120

基本存储器:0x00001114至0x00001118

因此,派生类必须在构造中分配 BEFORE 基础。派生类必须在销毁中取消分配 AFTER 基础。

如果您有以下代码:

class Base 
{
public:
    Base()
    {
        std::cout << "\n  Base created";
    }
    virtual ~Base()
    {
        std::cout << "\n  Base destroyed";
    }
}

class Derived : public Base 
{
public:
    Derived()
    // Derived is allocated here 
    // then Base constructor is called to allocate base and prepare it
    {
        std::cout << "\n  Derived created";
    }
    ~Derived()
    {
        std::cout << "\n  Derived destroyed";
    }   
    // Base destructor is called here
    // then Derived is deallocated
}

因此,如果您创建Derived d;并使其超出范围,那么您将在@Brian的答案中获得输出。但是内存中的对象行为实际上不是同一个顺序,它更像是这样:

结构:

  1. 派生分配

  2. 分配基地

  3. 名为

  4. 的基础构造函数
  5. 派生的构造函数名为

  6. 破坏:

    1. 派生的析构函数

    2. 名为

    3. 的基础析构函数
    4. Base deallocated

    5. 派生解除分配

答案 4 :(得分:0)

这在order-dtors-for-members上有清楚的描述。基本上,规则是“首先构造,最后破坏”。

构造函数调用顺序:

  1. base的构造函数按照出现在“:”之后的顺序被调用。
  2. 派生的类成员的构造函数按照出现的顺序并在类的构造函数之前被调用

析构函数的调用顺序与所构造函数的顺序相反。

示例:

#include <iostream>

struct base0 {  base0(){printf("%s\n", __func__);};~base0(){printf("%s\n", __func__);}; };
struct base1 { base1(){printf("%s\n", __func__);}; ~base1(){printf("%s\n", __func__);};};
struct member0 { member0(){printf("%s\n", __func__);};  ~member0(){printf("%s\n", __func__);};};
struct member1 { member1(){printf("%s\n", __func__);}; ~member1(){printf("%s\n", __func__);};};
struct local0 { local0(){printf("%s\n", __func__);}; ~local0(){printf("%s\n", __func__);}; };
struct local1 { local1(){printf("%s\n", __func__);};  ~local1(){printf("%s\n", __func__);};};
struct derived: base0, base1
{
  member0 m0_;
  member1 m1_;
  derived()
  {
    printf("%s\n", __func__);
    local0 l0;
    local1 l1;
  }
  ~derived(){printf("%s\n", __func__);};
};
int main()
{
  derived d;
}

输出:

base0
base1
member0
member1
derived
local0
local1
~local1
~local0
~derived
~member1
~member0
~base1
~base0