Skip to content

Latest commit

 

History

History
240 lines (175 loc) · 8.06 KB

17.3 — Order of construction of derived classes.md

File metadata and controls

240 lines (175 loc) · 8.06 KB

17.3 — Order of construction of derived classes

In the previous lesson on basic inheritance in C++, you learned that classes can inherit members and functions from other classes. In this lesson, we’re going to take a closer look at the order of construction that happens when a derived class is instantiated.

在前面的学习中你已经知道你可以通过继承来接收基类中的成员。在这一节我们要看一下在继承过程中的构造顺序。

First, let’s introduce some new classes that will help us illustrate some important points.

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; }
};

In this example, class Derived is derived from class Base.

img

Because Derived inherits functions and variables from Base, you may assume that the members of Base are copied into Derived. However, this is not true. Instead, we can consider Derived as a two part class: one part Derived, and one part Base.

因为派生类继承了基类中的函数和变量,所以你可能会假定基类中的成员被拷贝到了派生类中。事实上这并不是真的。我们可以把基类看成两个部分,一部分是派生类,一部分是基类。

img

You’ve already seen plenty examples of what happens when we instantiate a normal (non-derived) class:

你已经在前面的例子中看到过很多次当我们实例化一个对象的时候会发生什么了。

int main()
{
    Base base;
 
    return 0;
}

Base is a non-derived class because it does not inherit from any other classes. C++ allocates memory for Base, then calls Base’s default constructor to do the initialization.

Base不是一个派生类,因为他没有从任何类继承任何东西。

Now let’s take a look at what happens when we instantiate a derived class:

int main()
{
    Derived derived;
 
    return 0;
}

If you were to try this yourself, you wouldn’t notice any difference from the previous example where we instantiate non-derived class Base. But behind the scenes, things happen slightly different. As mentioned above, Derived is really two parts: a Base part, and a Derived part. When C++ constructs derived objects, it does so in phases. First, the most-base class (at the top of the inheritance tree) is constructed first. Then each child class is constructed in order, until the most-child class (at the bottom of the inheritance tree) is constructed last.

如果你自己试了一下的话,你就会发现这个前面没什么区别。但是在表象的背后,事情有一点不一样。就像上面提到的。派生类有两个部分,一个基类的部分,一个是派生的部分。当C++构造派生的派生对象的时候,它会循着这样的步骤去做:首先,最基类的基类(最上面的那个)会最先构造,然后每一个儿子再按顺序的构造。

So when we instantiate an instance of Derived, first the Base portion of Derived is constructed (using the Base default constructor). Once the Base portion is finished, the Derived portion is constructed (using the Derived default constructor). At this point, there are no more derived classes, so we are done.

当我们实例化一个派生类对象的时候。一旦基类的部分创建完成,派生类的部分开始创建。在这里,没有更多的派生类了,所以我们的活干完了。

This process is actually easy to illustrate.

过程实际上很简单。

#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;
}

This program produces the following result:

Instantiating Base
Base
Instantiating Derived
Base
Derived

As you can see, when we constructed Derived, the Base portion of Derived got constructed first. This makes sense: logically, a child can not exist without a parent. It’s also the safe way to do things: the child class often uses variables and functions from the parent, but the parent class knows nothing about the child. Instantiating the parent class first ensures those variables are already initialized by the time the derived class is created and ready to use them.

在我们构造派生类对象的时候,基类的对象首先被创建。如果没有双亲,孩子就不会存在。而且在别的方面也很安全。子类要用父类的变量和函数(如果这些变量和函数还不存在,怎么用?)父类不知道关于子类的事情。首先实例化父类对象确保了在派生类对象要用的父类对象的成员的时候,父类已经准备好被子类使用了。

Order of construction for inheritance chains

It is sometimes the case that classes are derived from other classes, which are themselves derived from other classes. For example:

有时候一个类A会继承另一个继承了 别的类C 的类B。(这里ABC描述的不是下面代码中的关系)

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";
    }
};

Remember that C++ always constructs the “first” or “most base” class first. It then walks through the inheritance tree in order and constructs each successive derived class.

记住C++总是首先构建最基础的类。然后它按着继承树的路走,连续的构造路上的每一个类。

Here’s a short program that illustrates the order of creation all along the inheritance chain.

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;
}

This code prints the following:

Constructing A:
A
Constructing B:
A
B
Constructing C:
A
B
C
Constructing D:
A
B
C
D

Conclusion

C++ constructs derived classes in phases, starting with the most-base class (at the top of the inheritance tree) and finishing with the most-child class (at the bottom of the inheritance tree). As each class is constructed, the appropriate constructor from that class is called to initialize that part of the class.

C++构造派生类对象分几个过程,从最基类的指向开始,直到完成最派生的那个子类结束。随着每一个类的构造成功,那个类调用适合的构造函数来进行初始化。

You will note that our example classes in this section have all used base class default constructors (for simplicity). In the next lesson, we will take a closer look at the role of constructors in the process of constructing derived classes (including how to explicitly choose which base class constructor you want your derived class to use).

你注意到我们这一节的内容都使用了默认构造函数 来保持简单。在下一节中,我们会更仔细的观察构造函数在构造派生类中扮演的角色。

包括应该怎么去明确的选取哪一个基类构造函数拿过来用。