Завершить Виртуальное наследование

В моем коде у меня есть базовый ромбовидный узор:

     CommonBase
        /  \
       /    \
 DerivedA  DerivedB
       \    /
        \  /
       Joined

Это реализовано следующим образом: общий базовый класс имеет конструктор по умолчанию и конструктор, принимающий параметр:

struct CommonBase {
    CommonBase() : CommonBase(0) {}

    CommonBase(int val) : value(val) {}

    const int value;
};

struct DerivedA : public virtual CommonBase {
    void printValue() {
        std::cout << "The value is " << value << "\n";
    }
};

struct DerivedB : public virtual CommonBase {
    void printValueTimes2() {
        std::cout << "value * 2 is " << value * 2 << "\n";
    }
};

struct Joined : public DerivedA,
                public DerivedB {
    Joined(int val) : CommonBase(val) {
        std::cout << "Constructor value is " << val << "\n";
        std::cout << "Actual value is " << value << "\n";
    }
};

Joined Класс инициализирует виртуальную базу с помощью конструктора, который принимает параметр, и все работает как положено.

Тем не менее, когда я выведу класс из Joined класс, что-то странное происходит - CommonBaseконструктор по умолчанию вызывается, если я явно не инициализирую CommonBase в конструкторе производных классов.

Это демонстрируется с помощью этого кода:

struct JoinedDerivedA : public Joined {
    JoinedDerivedA() : Joined(99) {
        printValue();
    }
};

struct JoinedDerivedB : public Joined {
    JoinedDerivedB() : Joined(99), CommonBase(99) {
        printValue();
    }
};

int main() {
    std::cout << "======= Joined =======\n";
    Joined j(99);
    j.printValue();
    std::cout << "\n=== JoinedDerivedA ===\n";
    JoinedDerivedA a;
    std::cout << "\n=== JoinedDerivedB ===\n";
    JoinedDerivedB b;

    return 0;
}

Выход этого кода

======= Joined =======
Constructor value is 99
Actual value is 99
The value is 99

=== JoinedDerivedA ===
Constructor value is 99
Actual value is 0 // <-- unexpected behaviour
The value is 0

=== JoinedDerivedB ===
Constructor value is 99
Actual value is 99
The value is 99

Почему это так? Можно ли не выполнять явную инициализацию общего базового класса в производных классах снова?

Вот код для ideone, поэтому вы можете запустить его самостоятельно: https://ideone.com/Ie94kb

1 ответ

Решение

Это указано в разделе "Инициализация баз и членов" [class.base.init] (12.6.2 в черновике n4567). Мы можем прочитать в §13 (подчеркните мой):

(13) В не делегирующем конструкторе инициализация происходит в следующем порядке:

(13.1) - Во-первых, и только для конструктора самого производного класса (1.8), виртуальные базовые классы инициализируются в том порядке, в котором они отображаются при обходе слева направо по глубине направленного ациклического графа базовых классов, где "слева направо" - порядок появления базовых классов в списке базовых спецификаторов производного класса.

(13.2) - Затем прямые базовые классы инициализируются в порядке объявления, как они появляются в списке базовых спецификаторов (независимо от порядка mem-initializer).

(13.3) - Затем не статические члены-данные инициализируются в том порядке, в котором они были объявлены в определении класса (опять же, независимо от порядка mem-инициализаторов).

(13.4) - Наконец, составной оператор тела конструктора выполняется.

[Примечание. Порядок декларирования должен обеспечивать уничтожение базовых и дочерних подобъектов в обратном порядке инициализации. —Конечная записка]

Это означает, что виртуальный базовый класс будет инициализирован до инициализации Joined, Так в DerivedJoinedA, по умолчанию инициализируется с value 0. Затем при инициализации Joinedинициализация CommonBase игнорируется, потому что он уже был инициализирован и value сохраняет значение 0

Вот почему вы должны инициализировать виртуальные базовые классы в наиболее производном классе.

Другие вопросы по тегам