частное наследование и сочинение, какое из них лучше и почему? - PullRequest
4 голосов
/ 24 января 2010

предположим, у меня есть класс engin, и я наследую классную машину от класса engin

class engin
{
    public:
        engin(int nobofcylinders);
        void start();
};
class car:private engin
{
    public:
        car():e(8){}
        void start()
        {
            e.start();
        }
    private:
        engin e;
};

теперь то же самое можно сделать с помощью композиции, вопрос в том, какой подход будет наилучшим и чаще всего используется в программировании, и почему ???????

Ответы [ 8 ]

6 голосов
/ 24 января 2010

Композиция должна быть предпочтительной по двум основным причинам:

  • составляемая вещь может иметь имена
  • Вы можете составить более одной вещи одного типа
5 голосов
/ 24 января 2010

Если вы хотите сравнить частное наследование с составом, прочитайте http://www.parashift.com/c++-faq-lite/private-inheritance.html#faq-24.3. Я не думаю, что частное наследование хорошо.


A Car имеет Engine, но Car is-not-an Engine, так что это лучше сделать с композицией. 1014 *

Наследование полезно для отношений "is-a", например, Bus is-a Car, a Car is-a vehicle и т. д.

Композиция полезна для отношений "есть", например, Car имеет Wheel -с, Car имеет Engine и т. д.

Таким образом, логический код должен быть похож на

class Car : public Vehicle {
    Engine engine;
    Wheel  wheels[4];
    ...
};
4 голосов
/ 24 января 2010

Я предпочитаю думать о наследовании как derived is a kind of base, что в основном означает публичное наследование. В случае частного наследования это больше похоже на derived has a base, что ИМХО звучит неправильно, потому что это ИМХО работа для композиции, а не наследование любого рода. Итак, поскольку частное наследование и состав по сути логически означают одно и то же, что выбрать? С примером, который вы опубликовали, я наверняка пойду на композицию. Зачем? Я склонен думать о всех видах наследования как о kind of отношениях, и с примером, который вы опубликовали, я не могу представить себе ситуацию, в которой я мог бы сказать a car is kind of an engine, это просто не так. Это действительно похоже на a car has an engine, так почему же автомобиль наследуется от двигателя? Я не вижу причин.

Теперь, действительно, есть случаи, когда хорошо иметь частное наследование, а именно boost::noncopyable, с защищенным его ctor / dtor, вам будет трудно создать его экземпляр, и действительно, поскольку мы хотим, чтобы наш класс have a noncopyable part , это единственный путь.

Некоторые руководства по стилю (например, руководство по стилю google c ++ ) даже рекомендуют никогда не использовать частное наследование по причинам, аналогичным тому, что я уже писал - частное наследование немного сбивает с толку.

Надеюсь, это поможет.

3 голосов
/ 24 января 2010

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

Композиция имеет много преимуществ по сравнению с частным наследованием:

  • Вы можете иметь более одного экземпляра определенного класса.
  • Вы не загрязняете пространство имен вашего класса множеством частных функций, которые не имеют смысла для вашего класса.
  • Вы можете дать названия частям вашего объекта
  • Ваш класс менее связан с классами, из которых он состоит, чем с классом, который он наследует от
  • Если вы обнаружите, что вам нужно поменять объект, который вы включили в композицию, при жизни вашего объекта, вы можете, с помощью частного наследования, вы застряли.

Есть много других преимуществ для композиции. По сути, он более гибкий и чистый.

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

  • Вы можете переопределить виртуальные функции.
  • Вы можете получить доступ к защищенным членам базового класса.
  • Вам нужно передать себя чему-то, что хочет объект класса, от которого вы наследуете (это обычно идет рука об руку с переопределением виртуальных функций).

И есть несколько довольно хитрых:

  • Если вы используете композицию для класса, который имеет размер 0, он все равно занимает место, но с частным наследованием - нет.
  • Вы хотите вызвать конкретный конструктор для виртуального базового класса класса, от которого вы собираетесь наследовать в частном порядке.
  • Если вы хотите инициализировать частную базу до инициализации других базовых классов (с композицией все переменные в вашем классе будут инициализированы после того, как все ваши базовые классы будут).
  • Использование частного виртуального наследования, чтобы убедиться, что есть только одна копия вещи, даже если у вас есть несколько базовых классов, у которых она есть. (На мой взгляд, это лучше решить с помощью указателей и нормальной композиции.)
3 голосов
/ 24 января 2010

Обычно состав предпочтительнее (другие указали основные причины), но частное наследование позволяет делать то, что нельзя сделать составом:

  • Оптимизация базового класса нулевого размера (базовый класс нулевого размера не увеличит размер класса, а член нулевого размера), что не является причиной его использования для классов политики, которые часто имеют нет данных

  • управление порядком инициализации, так что то, что составлено, инициализируется перед общедоступной базой

  • переопределение виртуального члена в том, что состоит

  • с частным виртуальным наследованием, гарантирующим, что существует только одна составная вещь, даже если вы делаете это в нескольких базах

Обратите внимание, что для двух последних применений факт существования базового класса можно наблюдать у потомка.

3 голосов
/ 24 января 2010

Частное наследство, несмотря на название, на самом деле не является наследством - по крайней мере, извне (класса), где оно имеет значение.

По этой причине применяются другие правила. Говорят, что в C ++ частное наследование моделирует отношения «реализовано в терминах». Таким образом, очередь с приоритетами, которая реализована в виде кучи, может выглядеть так:

template <typename T, typename Comp = std::less<T> >
class priority_queue : private heap<T, Comp> {
    // …
};

Лично я не вижу преимущества этого шаблона, и Нил уже заявил, что в большинстве случаев композиция фактически имеет преимущество перед частным наследованием.

Однако существует одно преимущество: поскольку это такой установленный шаблон, значение частного наследования сразу становится понятным опытному программисту на C ++; приведенный выше код скажет им, что очередь с приоритетами реализована в виде кучи - что было бы неочевидно, если бы классу просто удалось использовать кучу в качестве одного из его членов.

Частное наследование имеет тенденцию использоваться в C ++ главным образом для классов политики. Классическим примером являются распределители, которые определяют, как класс контейнера управляет внутренним хранением:

template <typename T, typename A = std::allocator<T> >
class vector : private A {
    // …
};

Никакого вреда не причинено. Но опять же, это можно было бы сделать и с помощью композиции.

1 голос
/ 24 января 2010
  • Частное наследство реализовано в терминах. Это обычно уступает по составу, но это имеет смысл, когда производный класс нужен доступ к защищенному базовому классу члены или необходимо переопределить унаследованные виртуальные функции.

  • В отличие от композиции, частной наследование может включить пустую базу оптимизация. Это может быть важно для разработчиков библиотек, которые стремятся минимизировать размеры объекта.

Скотт Мейерс, «Эффективный C ++», третье издание.

0 голосов
/ 24 января 2010

Базовые классы злые.

На мой взгляд, хороший дизайн ОО - это инкапсуляция и интерфейсы. Удобство базовых классов не стоит той цены, которую вы платите.

Вот действительно хорошая статья об этом:

http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...