Правильно ли предположить, что наследование алмазов нарушает инкапсуляцию в 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 в конечном итоге примет. Это всего лишь предложение, учитываемое только в том случае, если нет никаких основополагающих обстоятельств. Самый производный объект в конечном итоге решает, как будут инициализированы его виртуальные базы. Любой класс с виртуальной базой данных с состоянием должен быть готов к этому.

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