继承中调用构造函数/析构函数的顺序
关于创建对象的一个小问题。假设我有这两个类:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
类
开始。如果有多个基类,则从最左边的基类开始构建。 (旁注:如果存在虚拟
继承,那么它会被赋予更高的优先级)。声明它们的顺序
class
本身无论初始值设定项列表如何,调用顺序将如下所示:
class A
的构造函数class B
名为a
的字段(类型为class A
)将被构造class B
> 的构造函数class
. If there are multiple baseclass
es then, construction starts with the left most base. (side note: If there is avirtual
inheritance then it's given higher preference).order they are declared
class
itself is constructedIrrespective of the initializer list, the call order will be like this:
class A
's constructorclass B
's field nameda
(of typeclass A
) will be constructedclass B
's constructor假设没有虚拟/多重继承(这使事情变得相当复杂),那么规则很简单:
构造函数代码需要记住的一件重要事情是,直到第 4 步,该对象还不是其类的实例,因为它仅在构造函数开始执行后才获得此名称。这意味着,如果在成员的构造函数期间抛出异常,则不会执行该对象的析构函数,而只会销毁已经构造的部分(例如成员或基类)。这也意味着,如果在成员或基类的构造函数中调用对象的任何虚拟成员函数,则调用的实现将是基函数,而不是派生函数。
另一件需要记住的重要事情是,初始化列表中列出的成员将按照它们在类中声明的顺序构建,而不是按照它们在初始化列表中出现的顺序(幸运的是,如果您列出成员,大多数像样的编译器都会发出警告)以与类声明不同的顺序)。
另请注意,即使在构造函数代码执行期间,
this
对象已经获得了其最终类(例如,相对于虚拟分派),该类的析构函数也不会被调用,除非构造函数 完成执行。只有当构造函数完成执行时,对象实例才是实例中真正的一等公民……在此之前,它只是一个“想要的实例”(尽管具有正确的类)。销毁以完全相反的顺序发生:首先执行对象析构函数,然后它丢失其类(即从此时开始,该对象被视为基对象),然后所有成员以相反的声明顺序销毁,最后是基类销毁过程执行到最抽象的父级。至于构造函数,如果您在基类或成员析构函数中调用对象的任何虚拟成员函数(直接或间接),则执行的实现将是父实现,因为当类析构函数完成时,对象丢失了其类标题。
Assuming there is not virtual/multiple inheritance (that complicates things quite a bit) then the rules are simple:
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.
基类总是在数据成员之前构造。数据成员按照它们在类中声明的顺序构造。这个顺序与初始化列表无关。当数据成员被初始化时,它将在初始化列表中查找参数,如果不匹配则调用默认构造函数。数据成员的析构函数总是以相反的顺序调用。
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.
基类构造函数总是先执行。因此,当您编写语句
B b;
时,首先调用A
的构造函数,然后调用B
类构造函数因此构造函数的输出将按如下顺序排列:Base class constructor always executes first.so when you write a statement
B b;
the constructor ofA
is called first and then theB
class constructor.therefore the output from the constructors will be in a sequence as follows:本例的答案是 2531。这里如何调用构造函数:
相同方式的析构函数:
4 B::~A() 被调用。, m_i = 1
在此示例中,构造 m_A1 & 。 m_A2 与初始化列表顺序无关,与声明顺序无关。
The answer in this case is 2531. How constructor are called here:
The same-way Destructor is called:
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.
修改后的代码的输出是:
Output from the modified code is: