从成员初始化程序列表中调用基本方法

问题描述

从成员初始化程序列表中调用非虚拟基本方法是否安全?和虚拟的?

解决方法

在初始化所有基础之前,调用任何成员函数(虚拟或非虚拟)都不安全。以下是标准([class.base.init]§16)中给出的示例:

class A {
public:
  A(int);
};

class B : public A {
  int j;
public:
  int f();
  B() : A(f()),// undefined behavior: calls member function but base A not yet initialized
  j(f()) { }        // well-defined: bases are all initialized
};

class C {
public:
  C(int);
};

class D : public B,C {
  int i;
public:
  D() : C(f()),// undefined behavior: calls member function but base C not yet initialized
  i(f()) { }        // well-defined: bases are all initialized
};

还有更多subtle cases

,

正如我在评论中所说的:

在派生类的初始化程序列表中初始化的第一件事是基类。明确地看起来像这样:

class A{ ... };

class B : public A {
    int x,y;
    B() : A{},x{...},y{...} {
        ...
    }
};

因此,在初始化xy时,您可以调用A的任何非虚拟方法,因为它已经构造。

问题的第二部分与virtual ness并没有多大关系,它只是一个问题,即是否可以在构造函数中调用成员函数。答案是是,但是-您需要确保不使用对象的任何未初始化部分。

例如

struct Base {
    virtual int f(int i) = 0;
};

struct Derived : public Base {
    int x;
    int y;

    virtual int f(int i) override { return i; }

    Derived(int i) : Base{},x{f(i)},y{f(x)} {}
};

很好,但是写... Derived(int i) : Base{},x{f(y)},y{f(i)} ...不好。