Завершить Виртуальное наследование - PullRequest
0 голосов
/ 17 декабря 2018

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

     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 Ответ

0 голосов
/ 17 декабря 2018

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

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

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

(13.2) - Затем прямые базовые классы

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...