Зачем нам на самом деле нужно Private или Protected inheritance в C ++?

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

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

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

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

     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 }; 

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

    • ваши намерения гораздо менее ясны
    • это может привести к оскорбительному множественному наследованию
    • он прерывает инкапсуляцию classа Engine, так как вы можете получить доступ к его защищенным членам
    • вам разрешено переопределять виртуальные методы Engine, что вам не нужно, если ваша цель – простая композиция

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

    Является ли частичная classификация шаблонов ответом на эту проблему дизайна? ,

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

     struct noncopyable { private: noncopyable(noncopyable const&); noncopyable & operator=(noncopyable const&); }; class my_noncopyable_type : noncopyable { // ... }; 

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

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

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

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

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

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

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

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

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

    Я однажды применил эти структуры данных как classы:

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

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

     template  class CBigArray : public IArray, private CLnkList { // ... 
    Давайте будем гением компьютера.