Как мне избежать вызова конструктора по умолчанию в инициализаторах базового класса? - PullRequest
2 голосов
/ 03 апреля 2019

Я пытаюсь сконструировать Derived3 таким образом, чтобы при инициализации d2 вызывались конструкторы не по умолчанию.Я ожидал, что при инициализации d2 не будет вызван ни один из конструкторов по умолчанию.С этим кодом:

#include <string>
#include <iostream>

struct Base
{
    Base() : _message("Value initialized by default constructor")
    {
        std::cout << "Base default constructor called" << std::endl;
    }
    Base(std::string message) : _message(message)
    {
    }

    std::string     _message;
};

struct Derived1 : virtual public Base
{
    Derived1() : Base()
    {
        std::cout << "Derived1 default constructor called" << std::endl;
    }
    Derived1(std::string message) : Base(message)
    {   
    }

};

struct Derived2 : virtual public Base
{
    Derived2() : Base()
    {
        std::cout << "Derived2 default constructor called" << std::endl;
    }
    Derived2(std::string message) : Base(message)
    {
    }
};

struct Derived3 : virtual public Derived1, virtual public Derived2
{
    Derived3() : Derived1(), Derived2()
    {
        std::cout << "Derived3 default constructor called" << std::endl;
    }
    Derived3(std::string message) : Derived1(message), Derived2(message)
    {
    }
};

int main()
{
    Derived3 d1 = Derived3();
    std::cout << d1._message << std::endl; // You get what you expect.

    Derived3 d2 = Derived3("Not initialized by default constructor");
    std::cout << d2._message << std::endl; // You get what you do not expect.
}

Я бы ожидал, что d2._message будет "Not initialized by default constructor", тогда как на самом деле это "Value initialized by default constructor".Полный вывод:

Base default constructor called
Derived1 default constructor called
Derived2 default constructor called
Derived3 default constructor called
Value initialized by default constructor
Base default constructor called
Value initialized by default constructor

Ожидаемый вывод:

Base default constructor called
Derived1 default constructor called
Derived2 default constructor called
Derived3 default constructor called
Value initialized by default constructor
Not initialized by default constructor

Почему это происходит и как мне достичь ожидаемого поведения?

1 Ответ

3 голосов
/ 03 апреля 2019

Когда вы фактически наследуете базовый класс, во всех случаях виртуально унаследованный базовый класс можно всегда рассматривать как непосредственный суперкласс так называемого «наиболее производного» класса.Измените ваш конструктор Derived3 следующим образом:

Derived3(std::string message) : Derived1(message), Derived2(message),
                Base(message)

Base на самом деле также является базовым классом вашего Derived3, поскольку он фактически наследуется от Derived1Derived2).Вот что такое виртуальное наследование.

Если вы не хотите, чтобы Base создавался по умолчанию, вам нужно вызвать соответствующий конструктор самостоятельно, здесь.

Даже если вы этого не делаетеявным образом объявить Derived3 наследующим от Base, он наследует его виртуально, следовательно, вы можете вызвать его конструктор из Derived3.

Обратите внимание, что если вы объявите Derived4 как подкласс Derived3,этот конструктор Base здесь не будет вызван.Derived4 будет фактически наследовать Base и будет отвечать за его создание.

Что действительно происходит, когда у вас есть виртуально унаследованные классы, это то, что каждый конструктор, который вы объявляете, может рассматриваться как фактически приводящий к двум фактическим конструкторам, в действительности: конструктору, который отвечает за создание всех виртуально унаследованных классов, и конструктору, который этого не делает.Этот Derived3 конструктор, который вы объявили выше: в итоге вы получите два конструктора из этого.Два по цене одного: один, который будет строить Base, и тот, который не будет.Тот, который будет создавать Base, используется, когда Derived3 создается непосредственно, и является наиболее производным классом.Второй конструктор такой же, за исключением того, что он не будет создавать Base, и он будет использован, если будет создан экземпляр подкласса Derived3.Вы видите его как один конструктор, но компилятор выполняет гораздо больше работы, создает два из них и гарантирует, что правильный будет использован, когда что-то нужно будет построить.

...