请考虑下面的示例代码:
#include <iostream>
using namespace std;
class base
{
public:
base()
{
cout << "ctor in base class\n";
}
};
class derived1 : public base
{
public:
derived1()
{
cout <<"ctor in derived class\n";
}
};
int main()
{
derived1 d1obj;
return 0;
}
问题
When d1obj
创建后,按派生顺序调用构造函数:首先调用基类构造函数,然后调用派生类构造函数。这样做是因为以下原因吗:In-order to construct the derived class object the base class object needs to be constructed first
?
Does d1obj
包含基类对象?
我再添加一个问题
3)创建d1obj时,控件首先到达基类构造函数,然后到达派生类构造函数?或者是相反:它首先到达派生类构造函数,发现它有基类,因此控制权转到基类中的构造函数?
1)是的,首先构造基类,然后构造非静态数据成员,然后调用派生类的构造函数。原因是此类构造函数中的代码可以看到并使用完全构造的基类。
2)是的。您可以完全从字面上理解:在分配给派生类对象的内存中,有一个称为“基类子对象”的区域。派生类对象“包含”基类子对象的方式与它包含任何非静态数据成员的成员子对象的方式完全相同。实际上,问题中给出的示例恰好是一个特殊情况:“空基类优化”。即使该类型的完整对象也允许该基类子对象的大小为零base
大小永远不会为零。
不过,这种遏制是低级别的事情。正如其他人所说,这是真的从概念上来说基类与成员不同,并且语言的语法和语义以不同的方式对待它们,即使子对象本身只是类布局的一部分。
3)这是一个实现细节。基类构造函数主体中的代码在派生类构造函数主体中的代码之前执行,实际上,派生类构造函数然后在编译器生成的不可见的 try/catch 块中执行,以确保如果抛出异常,基类被破坏。但这取决于编译器如何根据发出的代码中的函数入口点实际执行的操作来实现此目的。
当类具有虚拟基时,构造函数通常会发出两个不同的函数体 - 一个在该类是最派生类型时使用,另一个在该类本身是基类时使用。原因是虚拟基类是由最派生的类构造的,以确保它们在共享时仅构造一次。因此,构造函数的第一个版本将调用所有基类构造函数,而第二个版本将仅调用非虚拟基类的构造函数。
编译器总是“知道”类具有哪些基数,因为您只能构造完整类型的对象,这意味着编译器可以看到类定义,并指定基数。所以不存在只有进入构造函数时才“发现它有基类”的问题——编译器knows它有一个基类,如果对基类构造函数的调用位于派生类构造函数代码中,那只是为了编译器的方便。它could在构造对象的每个地方发出对基类构造函数的调用,并且在派生类构造函数可以并且被内联的情况下,这就是最终效果。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)