了解基础 class 初始化

Understanding base class initialization

考虑程序:

#include<iostream>
#include<vector>

struct A
{
    int a;
    A(int) { }
    virtual int foo(){ std::cout << "base" << std::endl; return 5; }
};

struct B : A
{
    int b;
    B(): b(9), A(foo()) { }
    virtual int foo(){ std::cout << "derived" << std::endl; return 6; }
};

B b; //prints derived

int main(){ }

DEMO

Scott Meyers 在他的 Effective C++ 中所说的是:

During base class construction of a derived class object, the type of the object is that of the base class.

所以,我希望 base 被打印出来,因为我们在调用 foo 函数时处于基础 class class 构造之下。我错过了什么?也许是UB?如果是这样,请指出相关部分。

Scott Mayers 说的对,但是你的程序是错误的。

 B(): b(9), A(foo()) { } 

这个说法是绝对错误的:

在派生class构造函数初始化列表中,您应该在初始化派生class成员对象之前首先调用基础class构造函数。在实例化对象之前不能调用非静态成员函数。

#include<iostream>
#include<vector>

struct A
{
    int a;
    A(int) { std::cout<<"base constructed\n"; }
    virtual int foo(){ std::cout << "base" << std::endl; return 5; }
};

struct B : A
{
    int b;
    B():  A(6), b(9) { std::cout<<"derived constructed"; }
    virtual int foo(){ std::cout << "derived" << std::endl; return 6; }
};



int main(){
    B b; //prints derived
    }

O/P

base constructed

derived constructed

Scott 的意思是,当您在基础 class 构造函数中时,虚函数的常规规则不起作用。因此,如果您在基础 class 构造函数中,那么在构造函数内部调用的任何虚函数(该基础 class 的)都将在该构造函数中当前正在构造的对象上调用。

因此您的代码打印出正确的结果:foo()B 的构造函数中调用,而不是在父构造函数中调用。如果您在 A 构造函数中调用 foo inside,您将打印 base

根据标准,行为仍被视为未定义:

[12.6.2/13] Member functions (including virtual member functions, 10.3) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator (5.2.8) or of a dynamic_-cast (5.2.7). However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the resultof the operation is undefined.

但是你应该明白这里的"undefined"意味着你可能会在被调用的函数中使用一些内部状态。因为你不这样做,行为将是一致的,但标准仍然认为它是未定义的。 "undefined" 部分与打印的内容无关,而是与成员函数中可能访问的内容有关。

只是不要在构造函数中使用虚函数 - 第 9 项