继承中调用构造函数/析构函数的顺序

发布于 2024-12-06 16:43:13 字数 661 浏览 0 评论 0原文

关于创建对象的一个​​小问题。假设我有这两个类:

struct A{
    A(){cout << "A() C-tor" << endl;}
    ~A(){cout << "~A() D-tor" << endl;}
};

struct B : public A{
    B(){cout << "B() C-tor" << endl;}
    ~B(){cout << "~B() D-tor" << endl;}

    A a;
};

在 main 中我创建了一个 B 的实例:

int main(){
    B b;
}

请注意,B 派生自 A 并且还具有A 类型的字段。

我正在尝试找出规则。我知道,构造对象时首先调用其父构造函数,而析构时则相反。

字段(在本例中为 A a;)怎么样?当B创建时,什么时候会调用A的构造函数?我还没有定义初始化列表,是否有某种默认列表?如果没有默认列表呢?还有关于破坏的同样问题。

A little question about creating objects. Say I have these two classes:

struct A{
    A(){cout << "A() C-tor" << endl;}
    ~A(){cout << "~A() D-tor" << endl;}
};

struct B : public A{
    B(){cout << "B() C-tor" << endl;}
    ~B(){cout << "~B() D-tor" << endl;}

    A a;
};

and in main I create an instance of B:

int main(){
    B b;
}

Note that B derives from A and also has a field of type A.

I am trying to figure out the rules. I know that when constructing an object first calls its parent constructor, and vice versa when destructing.

What about fields (A a; in this case)? When B is created, when will it call A's constructor? I haven't defined an initialization list, is there some kind of a default list? And if there's no default list? And the same question about destructing.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

滥情哥ㄟ 2024-12-13 16:43:13
  • 构造总是从基开始。如果有多个基类,则从最左边的基类开始构建。 (旁注:如果存在虚拟继承,那么它会被赋予更高的优先级)。
  • 然后构造成员字段。它们被初始化在
    声明它们的顺序
  • 最后,构造 class 本身
  • 析构函数的顺序正好相反

无论初始值设定项列表如何,调用顺序将如下所示:

  1. Base class A 的构造函数
  2. class B 名为 a 的字段(类型为 class A)将被构造
  3. 为派生的 class B > 的构造函数
  • Construction always starts with the base class. If there are multiple base classes then, construction starts with the left most base. (side note: If there is a virtual inheritance then it's given higher preference).
  • Then the member fields are constructed. They are initialized in the
    order they are declared
  • Finally, the class itself is constructed
  • The order of the destructor is exactly the reverse

Irrespective of the initializer list, the call order will be like this:

  1. Base class A's constructor
  2. class B's field named a (of type class A) will be constructed
  3. Derived class B's constructor
携君以终年 2024-12-13 16:43:13

假设没有虚拟/多重继承(这使事情变得相当复杂),那么规则很简单:

  1. 分配对象内存
  2. 执行基类的构造函数,以大多数派生结束
  3. 执行成员初始化
  4. 对象成为真正的实例 其类的
  5. 执行

构造函数代码需要记住的一件重要事情是,直到第 4 步,该对象还不是其类的实例,因为它仅在构造函数开始执行后才获得此名称。这意味着,如果在成员的构造函数期间抛出异常,则不会执行该对象的析构函数,而只会销毁已经构造的部分(例如成员或基类)。这也意味着,如果在成员或基类的构造函数中调用对象的任何虚拟成员函数,则调用的实现将是基函数,而不是派生函数。
另一件需要记住的重要事情是,初始化列表中列出的成员将按照它们在类中声明的顺序构建,而不是按照它们在初始化列表中出现的顺序(幸运的是,如果您列出成员,大多数像样的编译器都会发出警告)以与类声明不同的顺序)。

另请注意,即使在构造函数代码执行期间,this 对象已经获得了其最终类(例如,相对于虚拟分派),该类的析构函数也不会被调用,除非构造函数 完成执行。只有当构造函数完成执行时,对象实例才是实例中真正的一等公民……在此之前,它只是一个“想要的实例”(尽管具有正确的类)。

销毁以完全相反的顺序发生:首先执行对象析构函数,然后它丢失其类(即从此时开始,该对象被视为基对象),然后所有成员以相反的声明顺序销毁,最后是基类销毁过程执行到最抽象的父级。至于构造函数,如果您在基类或成员析构函数中调用对象的任何虚拟成员函数(直接或间接),则执行的实现将是父实现,因为当类析构函数完成时,对象丢失了其类标题。

Assuming there is not virtual/multiple inheritance (that complicates things quite a bit) then the rules are simple:

  1. The object memory is allocated
  2. The constructor of base classes are executed, ending with most derived
  3. The member initialization is executed
  4. The object becomes a true instance of its class
  5. Constructor code is executed

One important thing to remember is that until step 4 the object is not yet an instance of its class, becuse it gains this title only after the execution of the constructor begins. This means that if there is an exception thrown during the constructor of a member the destructor of the object is not executed, but only already constructed parts (e.g. members or base classes) will be destroyed. This also means that if in the constructor of a member or of a base class you call any virtual member function of the object the implementation called will be the base one, not the derived one.
Another important thing to remember is that member listed in the initialization list will be constructed in the order they are declared in the class, NOT in the order they appear in the initialization list (luckily enough most decent compilers will issue a warning if you list members in a different order from the class declaration).

Note also that even if during the execution of constructor code the this object already gained its final class (e.g. in respect to virtual dispatch) the destructor of the class is NOT going to be called unless the constructor completes its execution. Only when the constructor completes execution the object instance is a real first class citizen among instances... before that point is only a "wanna-be instance" (despite having the correct class).

Destruction happens in the exact reverse order: first the object destructor is executed, then it loses its class (i.e. from this point on the object is considered a base object) then all members are destroyed in reverse declaration order and finally the base class destruction process is executed up to the most abstract parent. As for the constructor if you call any virtual member function of the object (either directly or indirectly) in a base or member destructor the implementation executed will be the parent one because the object lost its class title when the class destructor completed.

爱她像谁 2024-12-13 16:43:13

基类总是在数据成员之前构造。数据成员按照它们在类中声明的顺序构造。这个顺序与初始化列表无关。当数据成员被初始化时,它将在初始化列表中查找参数,如果不匹配则调用默认构造函数。数据成员的析构函数总是以相反的顺序调用。

Base classes are always constructed before data members. Data members are constructed in the order that they are declared in the class. This order has nothing to do with the initialization list. When a data member is being initialized, it will look through your initialization list for the parameters, and call the default constructor if there is no match. Destructors for data members are always called in the reverse order.

夜深人未静 2024-12-13 16:43:13

基类构造函数总是先执行。因此,当您编写语句 B b; 时,首先调用 A 的构造函数,然后调用 B 类构造函数因此构造函数的输出将按如下顺序排列:

A() C-tor
A() C-tor
B() C-tor

Base class constructor always executes first.so when you write a statement B b; the constructor of A is called first and then the B class constructor.therefore the output from the constructors will be in a sequence as follows:

A() C-tor
A() C-tor
B() C-tor
吻风 2024-12-13 16:43:13
#include<iostream>

class A
{
  public:
    A(int n=2): m_i(n)
    {
    //   std::cout<<"Base Constructed with m_i "<<m_i<<std::endl;
    }
    ~A()
    {
    // std::cout<<"Base Destructed with m_i"<<m_i<<std::endl; 
     std::cout<<m_i;
    }

  protected:
   int m_i;
};

class B: public A
{
  public:
   B(int n ): m_a1(m_i  + 1), m_a2(n)
   {
     //std::cout<<"Derived Constructed with m_i "<<m_i<<std::endl;
   }

   ~B()
   {
   //  std::cout<<"Derived Destructed with m_i"<<m_i<<std::endl; 
     std::cout<<m_i;//2
     --m_i;
   }

  private:
   A m_a1;//3
   A m_a2;//5
};

int main()
{
  { B b(5);}
  std::cout <<std::endl;
  return 0;
}

本例的答案是 2531。这里如何调用构造函数:

  1. B::A(int n=2) 调用构造函数
  2. B::B(5) 调用构造函数
  3. B.m_A1::A(3) 调用
  4. B。调用 m_A2::A(5) 调用

相同方式的析构函数:

  1. 调用 B::~B()。即 m_i = 2,将 A 中的 m_i 减为 1。
  2. 调用 B.m_A2::~A()。 m_i = 5
  3. B.m_A1::~A() 被调用。 m_i = 3
    4 B::~A() 被调用。, m_i = 1

在此示例中,构造 m_A1 & 。 m_A2 与初始化列表顺序无关,与声明顺序无关。

#include<iostream>

class A
{
  public:
    A(int n=2): m_i(n)
    {
    //   std::cout<<"Base Constructed with m_i "<<m_i<<std::endl;
    }
    ~A()
    {
    // std::cout<<"Base Destructed with m_i"<<m_i<<std::endl; 
     std::cout<<m_i;
    }

  protected:
   int m_i;
};

class B: public A
{
  public:
   B(int n ): m_a1(m_i  + 1), m_a2(n)
   {
     //std::cout<<"Derived Constructed with m_i "<<m_i<<std::endl;
   }

   ~B()
   {
   //  std::cout<<"Derived Destructed with m_i"<<m_i<<std::endl; 
     std::cout<<m_i;//2
     --m_i;
   }

  private:
   A m_a1;//3
   A m_a2;//5
};

int main()
{
  { B b(5);}
  std::cout <<std::endl;
  return 0;
}

The answer in this case is 2531. How constructor are called here:

  1. B::A(int n=2) constructor is called
  2. B::B(5) constructor is called
  3. B.m_A1::A(3) is called
  4. B.m_A2::A(5) is called

The same-way Destructor is called:

  1. B::~B() is called. i.e m_i = 2, which decrement m_i to 1 in A.
  2. B.m_A2::~A() is called. m_i = 5
  3. B.m_A1::~A() is called. m_i = 3
    4 B::~A() is called., m_i = 1

In this example, construction of m_A1 & m_A2 is irrelevant of order of initialization list order but their declaration order.

时光沙漏 2024-12-13 16:43:13

修改后的代码的输出是:

A() C-tor
A() C-tor
B() C-tor
~B() D-tor
~A() D-tor
~A() D-tor

Output from the modified code is:

A() C-tor
A() C-tor
B() C-tor
~B() D-tor
~A() D-tor
~A() D-tor
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文