Почему частное наследование увеличивает вероятность того, что кто-то нарушит мой код по сравнению с композицией? - PullRequest
0 голосов
/ 08 февраля 2012

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

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

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

Ответы [ 4 ]

2 голосов
/ 08 февраля 2012

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

2 голосов
/ 08 февраля 2012

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

1 голос
/ 08 февраля 2012

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

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

0 голосов
/ 08 февраля 2012

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

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