派生类的构造顺序
在上一节关于C ++基本继承的课程中,您了解到类可以从其他类继承成员和函数。在本课中,我们将仔细研究实例化派生类时发生的构造顺序。
首先,让我们介绍一些新的类,它们将帮助我们说明一些重要的观点。
class Base
{
public:
int m_id;
Base(int id=0)
: m_id(id)
{
}
int getId() const { return m_id; }
};
class Derived: public Base
{
public:
double m_cost;
Derived(double cost=0.0)
: m_cost(cost)
{
}
double getCost() const { return m_cost; }
};
在此示例中,类Derived派生自类Base。
因为Derived从Base继承函数和变量,所以您可以假设Base的成员被复制到Derived中。但是,事实并非如此。相反,我们可以将Derived视为两部分类:一部分Derived,一部分Base。
您已经看到了很多示例,当我们实例化一个普通(非派生)类时会发生什么:
int main()
{
Base base;
return 0;
}
Base是非派生类,因为它不从任何其他类继承。C ++为Base分配内存,然后调用Base的默认构造函数来进行初始化。
现在让我们看看实例化派生类时会发生什么:
int main()
{
Derived derived;
return 0;
}
如果你自己尝试一下,你就不会注意到我们实例化非派生类Base的前一个例子有什么不同。但在幕后,情况略有不同。如上所述,Derived实际上是两部分:基础部分和派生部分。当C ++构造派生对象时,它会分阶段进行。首先,首先构造最基类(在继承树的顶部)。然后按顺序构造每个子类,直到最后构造最多子类(在继承树的底部)。
因此,当我们实例化Derived实例时,首先构造Derived的Base部分(使用Base默认构造函数)。完成Base部分后,将构建Derived部分(使用Derived默认构造函数)。此时,没有更多的派生类,所以我们完成了。
这个过程实际上很容易说明。
#include <iostream>
class Base
{
public:
int m_id;
Base(int id=0)
: m_id(id)
{
std::cout << "Base\n";
}
int getId() const { return m_id; }
};
class Derived: public Base
{
public:
double m_cost;
Derived(double cost=0.0)
: m_cost(cost)
{
std::cout << "Derived\n";
}
double getCost() const { return m_cost; }
};
int main()
{
std::cout << "Instantiating Base\n";
Base cBase;
std::cout << "Instantiating Derived\n";
Derived cDerived;
return 0;
}
该程序产生以下结果:
Instantiating Base
Base
Instantiating Derived
Base
Derived
正如您所看到的,当我们构造Derived时,Derived的Base部分首先被构造。这是有道理的:从逻辑上讲,如果没有父母,孩子就不能存在。这也是安全的做事方式:子类通常使用父类的变量和函数,但父类对子进程一无所知。首先实例化父类可确保在创建派生类并准备使用它们时已初始化这些变量。
继承链的构造顺序
有时候类是从其他类派生的,这些类本身是从其他类派生的。例如:
class A
{
public:
A()
{
std::cout << "A\n";
}
};
class B: public A
{
public:
B()
{
std::cout << "B\n";
}
};
class C: public B
{
public:
C()
{
std::cout << "C\n";
}
};
class D: public C
{
public:
D()
{
std::cout << "D\n";
}
};
请记住,C ++总是首先构造“第一”或“最基础”类。然后按顺序遍历继承树并构造每个连续的派生类。
这是一个简短的程序,它说明了继承链中的创建顺序。
int main()
{
std::cout << "Constructing A: \n";
A cA;
std::cout << "Constructing B: \n";
B cB;
std::cout << "Constructing C: \n";
C cC;
std::cout << "Constructing D: \n";
D cD;
}
此代码打印以下内容:
Constructing A:
A
Constructing B:
A
B
Constructing C:
A
B
C
Constructing D:
A
B
C
D
Conclusion
C ++分阶段构造派生类,从最基类开始(在继承树的顶部),最后用子类(在继承树的底部)。在构造每个类时,调用该类中的适当构造函数来初始化该类的该部分。
您将注意到,本节中的示例类都使用了基类默认构造函数(为简单起见)。在下一课中,我们将仔细研究构造函数在构造派生类的过程中的作用(包括如何显式选择希望派生类使用哪个基类构造函数)。