Правильно ли предположить, что наследование алмазов нарушает инкапсуляцию в C++?
Посмотрите на следующий код:
class Base
{
int a;
public:
Base(int b){a =b;}
};
class M1: public virtual Base{
public:
M1(int a): Base(a+10){} // Expect a is increased by 10
};
class M2: public virtual Base{
public:
M1(int a): Base(a+20){} // Expect a is increased by 20
};
class F: public M1, public M2{
public:
F(int a): M1(a-2), M2(a-3), Base( a-10){} // ouch Base constructor called only once and with unexpected value!
};
Теперь, когда код действительно глуп, он выдвигает на первый план одну проблему: в основном, оба класса M1 и M2 для правильной работы предполагают, что Base находится в определенном состоянии (в этом случае он увеличивается на 10 или 20). Добавление другого производного класса (F) нарушает эту инкапсуляцию, потому что оставляет "a" в неожиданном состоянии, потому что оно уменьшает его на 10 вместо увеличения.
M1 и M2 получат доступ к "a" с неожиданным значением, тогда для меня это означает, что, по сути, я нарушил инкапсуляцию, люди больше не могут изменять код в классах M1,M2, потому что это может в конечном итоге нарушить F (также наоборот).
На самом деле то, что я прошу, является полной противоположностью
В проблеме хрупкого базового класса у нас есть "производный" класс, который нарушается изменениями базового класса, в моем случае это наоборот:
- У меня есть производный класс, который нарушает один из базовых классов.
1 ответ
Субобъект не владеет своей виртуальной базой. Он потенциально разделяет его с другими подобъектами того же самого производного объекта, который является конечным владельцем всех своих подобъектов виртуального базового класса.
Сказать, что это нарушает инкапсуляцию, не более и не менее правильно, чем сказать, что, например, std::shared_ptr нарушает инкапсуляцию. В обоих случаях состояние делится между несколькими пользователями, а не скрыто, что и является целью разработки каждой функции.
В вашем случае было бы просто неправильно со стороны M1 предполагать, что состояние, которое он дает Base, является состоянием, которое Base в конечном итоге примет. Это всего лишь предложение, учитываемое только в том случае, если нет никаких основополагающих обстоятельств. Самый производный объект в конечном итоге решает, как будут инициализированы его виртуальные базы. Любой класс с виртуальной базой данных с состоянием должен быть готов к этому.