Зачем нам на самом деле нужно Частное или Защищенное наследование в C ++?

StackOverflow https://stackoverflow.com/questions/374399

Вопрос

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

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

Действительно ли это полезно?

Это было полезно?

Решение

Это полезно, когда вы хотите иметь доступ к некоторым членам базового класса, но не раскрывая их в интерфейсе вашего класса.Частное наследование также можно рассматривать как своего рода композицию:в Часто задаваемые вопросы по C ++-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
};

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

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

Другие советы

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

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

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

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

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

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

Модели публичного наследования IS-A.
Модели непубличного наследования-РЕАЛИЗОВАНЫ-В-ТЕРМИНАХ-OF.
Модели сдерживания ИМЕЮТ-A, что эквивалентно IS-IMPLEMENTED-IN-TERMS-OF.

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

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

Частное наследование в основном используется по неправильной причине.Люди используют его для IS-IMPLEMENTED-IN-TERMS-OF, как указано в более раннем ответе, но, по моему опыту, всегда удобнее сохранять копию, а не наследовать от class .Другой более ранний ответ, касающийся CBigArray, представляет собой прекрасный пример этого антишаблона.

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

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

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

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

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

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

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

template <typename T>
class CBigArray : public IArray, private CLnkList {
    // ...
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top