Зачем нам на самом деле нужно частное или защищенное наследование в C ++? - PullRequest
57 голосов
/ 17 декабря 2008

В C ++ я не могу вспомнить случай, в котором я хотел бы наследовать частный / защищенный от базовый класс:

class Base;
class Derived1 : private Base;
class Derived2 : protected Base;

Это действительно полезно?

Ответы [ 7 ]

44 голосов
/ 17 декабря 2008

Это полезно, когда вы хотите иметь доступ к некоторым членам базового класса, но не раскрывая их в интерфейсе вашего класса. Частное наследование также можно рассматривать как некую композицию: C ++ faq-lite дает следующий пример, иллюстрирующий это утверждение

class Engine {
 public:
   Engine(int numCylinders);
   void start();                 // Starts this Engine
};

class Car {
  public:
    Car() : e_(8) { }             // Initializes this Car with 8 cylinders
    void start() { e_.start(); }  // Start this Car by starting its Engine
  private:
    Engine e_;                    // Car has-a Engine
};

Чтобы получить ту же семантику, вы также можете написать класс автомобиля следующим образом:

class Car : private Engine {    // Car has-a Engine
 public:
   Car() : Engine(8) { }         // Initializes this Car with 8 cylinders
   using Engine::start;          // Start this Car by starting its Engine
}; 

Однако у этого способа есть несколько недостатков:

  • ваши намерения гораздо менее ясны
  • это может привести к злоупотреблению множественным наследованием
  • нарушает инкапсуляцию класса Engine, поскольку вы можете получить доступ к его защищенным элементам
  • вам разрешено переопределять виртуальные методы Engine, чего вы не хотите, если ваша цель - простая композиция
40 голосов
/ 17 декабря 2008

Приват может быть полезен при многих обстоятельствах. Только один из них является политикой:

Является ли частичная специализация шаблона класса ответом на эту проблему проектирования? .

Другой случай, когда это полезно, это запрет на копирование и присвоение:

struct noncopyable {
    private:
    noncopyable(noncopyable const&);
    noncopyable & operator=(noncopyable const&);
};

class my_noncopyable_type : noncopyable {
    // ...
};

Поскольку мы не хотим, чтобы у пользователя был указатель типа noncopyable* на наш объект, мы производим его конфиденциально. Это относится не только к некопируемым, но и ко многим другим таким классам (политики являются наиболее распространенными).

15 голосов
/ 17 декабря 2008

Модели публичного наследования IS-A.
Модели непубличного наследования IS-IMPLEMENTED-IN-TERMS-OF.
Модели сдерживания HAS-A, что эквивалентно IS-IMPLEMENTED-IN-TERMS-OF.

Саттер по теме . Он объясняет, когда вы выберете непубличное наследование вместо содержания для деталей реализации.

3 голосов
/ 18 декабря 2008

Частное наследование в основном используется по неправильной причине. Люди используют его для «ВЫПОЛНЕНО В УСЛОВИЯХ», как указано в предыдущем ответе, но по моему опыту всегда лучше сохранять копию, а не наследовать от класса. Другой более ранний ответ, о CBigArray, является прекрасным примером этого анти-паттерна.

Я понимаю, что могут быть случаи, когда has-a не работает из-за чрезмерного усердного использования «защищенного», но лучше исправить поврежденный класс, чем сломать новый класс.

3 голосов
/ 17 декабря 2008

Например, когда вы хотите повторно использовать реализацию, но не интерфейс класса и не переопределить его виртуальные функции.

0 голосов
/ 17 декабря 2008

Я однажды реализовал эти структуры данных как классы:

  • Связанный список
  • Общий массив (аннотация)
  • Простой массив (наследуется от универсального массива)
  • Большой массив (наследуется от универсального массива)

Интерфейс большого массива сделал бы его похожим на массив, однако на самом деле это был связанный список простых массивов фиксированного размера. Итак, я объявил это так:

template <typename T>
class CBigArray : public IArray, private CLnkList {
    // ...
0 голосов
/ 17 декабря 2008

Я использовал как частное, так и защищенное наследование в тот или иной момент.

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

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

...