C++ 隐式和显式继承构造函数调用

C++ implicit and explicit inheritance constructor calls

我对基类构造函数的隐式和显式调用有疑问。如果我们有这样的 class 层次结构:

class Person{
    protected:
        std::string m_name;
    public:
        Person(std::string& _name) : m_name(_name){std::cout << "A person is being constructed." << std::endl;}
};

class Baby : public Person{
    private:
        int m_no_of_nappies;
    public:
        Baby(std::string& _name, int& _no_of_nappies) : m_no_of_nappies(_no_of_nappies), Person(_name) {std::cout << "A baby is being constructed." << std::endl ;}

};

根据我的讲义,主要是对 'Baby' 的调用,如下所示:

std::string babyname = "Robert";
int nappies = 5;

Baby baby(babyname, nappies);

导致以下情况发生:

  1. 由于在 Baby 的初始化列表中对 Person 进行了显式调用:调用了 Baby 的初始化列表,并初始化了 no_of_nappies
  2. 接下来,调用 Person 的构造函数,Person 的初始化列表被调用。 m_name 被初始化。
  3. 然后调用Person的构造体。
  4. 宝宝的构造函数体终于被调用了

这是有道理的,但是,如果对父 class 的默认构造函数进行隐式调用会怎么样:

class Vehicle{
    protected:
        int m_no_wheels;
    public:
        Vehicle() : m_no_wheels(0) { std::cout << "A vehicle is being constructed." << std::endl; }
};

class Bicycle : public Vehicle{
    protected:
        bool m_is_locked;
    public:
        Bicycle() : m_is_locked(false) { std::cout << "A bicycle is being constructed." << std::endl; }
};

这是我不太确定的部分。我最好的猜测是在 main 中调用 Bicycle bike; 具有以下效果:

  1. 从 Bike 隐式调用 Vehicle 的默认构造函数。 在自行车的初始化列表被调用之前。
  2. 由于载具不继承任何东西,载具的初始化列表被称为,它将m_no_wheels初始化为0
  3. 车辆的构造体被调用。
  4. 我们 return 到 Bicycle 并且 现在它的初始化列表被调用 ,初始化 m_is_lockedfalse
  5. Bike 的构造体被调用。

有人可以解释一下隐式调用背后的推理是否正确吗?

在我看来,主要区别在于,通过显式引用基本构造函数,子 class' 初始化列表总是首先被命中以调用该基本构造函数 - 但是,通过隐式调用,最上面的父级初始化列表总是首先被命中。

谢谢,非常感谢!

编辑: 我特别询问顺序是否会根据对父项的隐式或显式调用而改变 class.

基础和成员的初始化顺序在 [class.base.init]/11 中指定,您可以在此处找到摘要:http://en.cppreference.com/w/cpp/language/initializer_list#Initialization_order

The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:

  1. If the constructor is for the most-derived class, virtual base classes are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)
  2. Then, direct base classes are initialized in left-to-right order as they appear in this class's base-specifier list
  3. Then, non-static data members are initialized in order of declaration in the class definition.
  4. Finally, the body of the constructor is executed

(Note: if initialization order was controlled by the appearance in the member initializer lists of different constructors, then the destructor wouldn't be able to ensure that the order of destruction is the reverse of the order of construction)

在定义任何构造函数之前,初始化的顺序是固定的;构造函数初始化列表只影响如何 基类和成员被初始化,而不是它们被初始化的顺序。

因为 PersonBaby 的基础,它总是在 Baby 的成员 m_no_of_nappies 之前被初始化。作为Person初始化的一部分,初始化它自己的成员,然后执行它的构造函数体。在 Person 的构造函数体 returns 之后,m_no_of_nappies 被初始化。 (销毁总是以相反的顺序发生。)Vehicle 同样是 Bicycle 的基础,并且首先被初始化;因为它没有 mem-initializer,默认构造函数被调用。

§12.6.2 定义事物的初始化方式:

The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:

  • If the constructor is for the most-derived class, virtual base classes are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)
  • Then, direct base classes are initialized in left-to-right order as they appear in this class's base-specifier list
  • Then, non-static data members are initialized in order of declaration in the class definition.
  • Finally, the body of the constructor is executed (Note: if initialization order was controlled by the appearance in the member initializer lists of different constructors, then the destructor wouldn't be able to ensure that the order of destruction is the reverse of the order of construction)

针对您的情况总结(并撇开虚函数):

  1. Base class按照声明继承的顺序排列
  2. 成员按申报顺序

因此构造函数初始化列表中的顺序没有影响

在第一种情况下,您在这一点上是错误的:PersonBaby 的基础 class 并且在 m_no_of_nappies

之前被初始化

编辑: 你的问题

Baby calls Person from within its initialisation list, hence the first thing that's hit is Baby's initialisation list?

[class.base.init]/10 可能是您正在寻找的:您并不是真正的 "call" 基础 class构造函数(假设没有委托),它会在初始化派生对象时由编译器为您调用。

编译器会为您进行设置,以帮助保持构造函数和析构函数的顺序正确

The reason for ignoring the order of initializers is to preserve the usual FIFO ordering of constructor and destructor calls. Allowing two constructors to use different orders of initialization of bases and members would constrain implementations to use more dynamic and more expensive strategies

取自

最后

is the implicit call to the base class (that the compiler makes) done before the Bicycle's initialisation list or after?

在其余成员 class 初始化之前,如 §12.6.2。