cga*_*gao 0 c++ constructor base-class derived-class
我知道这个问题有明确的答案:首先调用基类构造函数,然后调用派生类构造函数.
但我并不完全理解"被叫"这个词.这是否意味着构造函数的使用开始,或构造函数的使用完成?换句话说,下面的代码有两种可能的顺序:
BaseClass构造函数启动 - > BaseClass构造函数完成 - > DerivedClass构造函数启动 - > DerivedClass构造函数完成.
DerivedClass构造函数启动 - > BaseClass构造函数启动 - > BaseClass构造函数完成 - > DerivedClass构造函数完成.
哪一个应该是正确的顺序?如果1是正确的,在初始化DerivedClass实例之前,编译器如何知道调用BaseClass构造函数?
似乎案例2是正确的:"被叫"应该意味着构造函数的完成.后续问题是析构函数如何?我知道标准答案是"派生类的析构函数首先被调用".那么哪个是正确的顺序:
谢谢
class BaseClass {
public:
BaseClass() {
cout << "BaseClass constructor." << endl;
}
};
class DerivedClass : public BaseClass {
public:
DerivedClass() : BaseClass() {
cout << "DerivedClass constructor." << endl;
}
};
int main() {
DerivedClass dc;
}
Run Code Online (Sandbox Code Playgroud)
[class.base.init]/10,强调我的:
在非委托构造函数中,初始化按以下顺序进行:
首先,仅对于派生类最多的构造函数,虚拟基类被初始化[...]
然后,直接基类按声明顺序初始化,因为它们出现在base-specifier-list中 (无论mem-initializers的顺序如何).
然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样不管mem-initializers的顺序如何 ).
最后,执行构造函数体的复合语句.
也就是说,派生类'ctor'首先被"调用",但在其复合语句(其函数体)之前,基类ctor必须完成.
我们可以看到这个顺序的一种方法是在派生类的ctor中使用function-try-block:
#include <iostream>
struct Base {
Base() { throw "Base throwing\n"; }
};
struct Derived : Base{
Derived()
try : Base()
{}
catch(char const* p) {
std::cout << p;
}
};
int main() {
try { Derived d; }catch(...){}
}
Run Code Online (Sandbox Code Playgroud)
该功能试块可以捕捉的基地和成员初始化过程中发生的异常.异常是隐式传播的:由于无法构造/初始化基类/成员,因此无法构造/初始化(派生)对象.
对于析构函数,[class.dtor]/8
在执行析构函数的主体并销毁在主体内分配的任何自动对象之后,类
X
的析构函数调用析构函数X
的直接非变量非静态数据成员,X
直接基类的析构函数,如果X
是最派生类的类型,它的析构函数调用析构函数X
的虚拟基类.调用所有析构函数,就好像它们是使用限定名称引用一样,即忽略更多派生类中的任何可能的虚拟覆盖析构函数.基础和成员按照构造函数完成的相反顺序销毁.
如果析构函数是虚拟的,则编译器(有时)无法知道必须在销毁站点/转换单元调用哪些析构函数(最派生类型及其基础).因此,析构函数本身必须调用基础和成员的破坏(至少,对于通过动态调度调用的虚拟析构函数).
归档时间: |
|
查看次数: |
2075 次 |
最近记录: |