множественное наследование c ++ и явные вызовы конструктора - PullRequest
0 голосов
/ 30 апреля 2018

Предположим, у нас есть родительский класс A, который имеет защищенные члены x и y и конструктор:

A::A(int xpos, int ypos) : x(xpos), y(ypos) {}

Теперь рассмотрим, что у нас есть два класса B и C, которые наследуются от A и имеют конструкторы, как определено ниже.

class B : public virtual A
B::B(int xpos, int ypos) : A(xpos,ypos) {}

class C : public virtual A
C::C(int xpos, int ypos) : A(xpos,ypos) {}    

Наконец, давайте создадим класс D, который наследует B и C.

class D : public B, public C

Если я пишу конструктор Ds следующим образом, я получаю ошибку компиляции, говорящую, что конструктор Ds должен явно вызывать конструктор As.

D::D(int xpos, int ypos) : B(xpos,ypos), C(xpos,ypos) {}  

Почему это? Конечно, это проблематично, если я пытаюсь наследовать от классов, что я ничего не знаю о конструкторе классов? Разве мало того, что B и C оба явно вызывают конструктор As?

Ответы [ 2 ]

0 голосов
/ 30 апреля 2018

Правило для виртуальной базы заключается в том, что самый производный класс вызывает его конструктор. Если это не так, какой из промежуточных базовых классов должен создать одну копию виртуальной базы? Представьте на мгновение, что конструктор для вашего класса B меняет порядок аргументов при построении A:

B::B(int xpos, int ypos) : A(ypos, xpos) {}

Теперь, каким должно быть состояние подобъекта A? Первый конструктор побеждает? Последний побеждает? В любом случае, это хрупко: изменение порядка B и C в вашем типе D изменит способ инициализации базового класса A.

0 голосов
/ 30 апреля 2018

Недостаточно, чтобы и B, и C явно вызывали конструктор A, потому что A наследуется "виртуально". Это означает, что содержимое одного экземпляра A «совместно используется» между B и C.

Если бы C ++ разрешил B или C инициализировать «общий» экземпляр A в одностороннем порядке, вы можете получить противоречивое поведение. Посмотрим, что произойдет, если конструктор D переключит порядок xpos и ypos в двух инициализаторах:

D::D(int xpos, int ypos) : B(xpos,ypos), C(ypos,xpos) {}

Так как B и C forward xpos и ypos to A::A, экземпляр A, совместно используемый между B и C, не будет инициализирован в едином ключе. Вот почему D::D требуется для явной инициализации виртуального экземпляра A.

Обратите внимание, что это требуется только при работе с виртуальным наследованием, поскольку часть экземпляров B и C является общей.

Конечно, это проблематично, если я пытаюсь унаследовать от классов то, что я ничего не знаю о конструкторе классов?

Требование вызова конструктора base-of-the-base нарушает инкапсуляцию. Однако, когда вы наследуете классы с виртуальными базами, ожидается, что наследник будет понимать не только структуру и поведение своих «родственных баз», но и взаимодействие между ними. Вот почему нарушение инкапсуляции можно считать оправданным, хотя и не идеальным.

...