Почему частное наследование увеличивает вероятность того, что кто-то взломает мой код по сравнению с композицией?

Автор этой статьи утверждает, что

«Обычно вы не хотите иметь доступ к внутренностям слишком многих других классов, и частное наследование дает вам часть этих дополнительных возможностей (и ответственности). это увеличивает вероятность того, что кто-то изменит что-то, что нарушит ваш код».

Предположим следующий код, где Car наследуется от Engine частным образом:

#include <iostream>
using namespace std;

class Engine
{
    int numCylinders;
    public:
    class exception{};
    Engine(int i) { if( i < 4 ) throw exception(); numCylinders = i; }
    void start() { cout << "Engine started " << numCylinders << " cylinders" << endl; }
};

class Car : private Engine          
{    
    public:
    Car(int i) : Engine(i) {}
    using Engine::start;
};

int main()
{
    try
    {
        Car c(4);
        c.start();
    }
    catch( Engine::exception& )
    {
        cout << "A Car cannot have less than 4 cylinders" << endl;
    }
}

Мой вопрос: как Car может сломать этот код, настроив, например, его Engine с менее чем 4 цилиндрами, используя частное наследование и без защищенных членов в базовом классе?


person Belloc    schedule 08.02.2012    source источник
comment
Это верно (по определенным определениям истины), но выражено плохо. Более тесно связанный код сложнее поддерживать.   -  person Martin York    schedule 08.02.2012


Ответы (4)


Я думаю, что проблема не в том, что Автомобиль может взломать код вашего двигателя, а в том, что, изменив двигатель, кто-то может взломать код вашего автомобиля. Наследование представляет собой гораздо более тесную связь, чем композиция, поэтому изменения в Engine с большей вероятностью нарушат класс, который наследуется от него, а не содержит его. В случае C++ еще более слабая связь достигается за счет того, что Car содержит указатель Engine или интеллектуальный указатель.

person Random Wombat    schedule 08.02.2012

Один из способов, при котором наследование создает гораздо более тесную связь, чем членство, заключается в том, что пространство имен производного и базового классов смешивается. Таким образом, значение имени в контексте производного класса зависит от того, какие имена вводит базовый класс, и существуют обычные эффекты переопределения/скрытия. Изменение в базовом классе может повлиять на код в производном классе, который не обязательно будет четко локализован или который сразу же даст полезную диагностику. Напротив, если интерфейс объекта-члена изменится, то самое большее, что может сломать, это код, который действительно упоминает этот объект-член.

person Kerrek SB    schedule 08.02.2012

Я не вижу, чтобы Car мог установить Engine::numCylinders (по крайней мере, не без грязных трюков, таких как доступ к необработанной памяти). В примере в статье используется защищенный метод, вы используете закрытый член.

Кстати, статья начинается со слов «Используйте композицию, когда можете» — и у автомобиля есть двигатель, но это не двигатель. Когда A происходит от B, это обычно означает, что A является B.

person Jörg Beyer    schedule 08.02.2012

Автор статьи в предыдущем пункте ссылается на некоторые «недостатки» использования частного наследования, здесь:

  • Вариант с простой композицией необходим, если вы хотите содержать несколько двигателей на машину.
  • Вариант частного наследования может ввести ненужное множественное наследование.
  • Вариант частного наследования позволяет членам Car преобразовывать Car* в Engine*.
  • Вариант частного наследования позволяет получить доступ к защищенным членам базового класса.
  • Вариант частного наследования позволяет Car переопределять виртуальные функции Engine.
  • Вариант с частным наследованием немного упрощает (20 символов по сравнению с 28 символами) предоставление Car метода start(), который просто вызывает метод start() Engine.
person jgsogo    schedule 08.02.2012