Зачем нам на самом деле нужно Private или Protected inheritance в С++?
В С++ я не могу придумать случай, в котором я хотел бы наследовать private/protected из
базовый класс:
class Base;
class Derived1 : private Base;
class Derived2 : protected Base;
Действительно ли это полезно?
Ответы
Ответ 1
Это полезно, если вы хотите иметь доступ к некоторым членам базового класса, но не подвергая их в своем интерфейсе класса. Частное наследование также можно рассматривать как своего рода композицию: С++ 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, что вам не нужно, если ваша цель - простая композиция.
Ответ 2
Частный может быть полезен в нескольких обстоятельствах. Только одна из них - политика:
Является ли частная спецификация шаблона шаблона ответом на эту проблему дизайна?.
Другим случаем, когда это полезно, является запрет копирования и назначения:
struct noncopyable {
private:
noncopyable(noncopyable const&);
noncopyable & operator=(noncopyable const&);
};
class my_noncopyable_type : noncopyable {
// ...
};
Поскольку мы не хотим, чтобы у пользователя был указатель типа noncopyable*
на наш объект, мы получаем конфиденциальность. Это важно не только для не подлежащих копированию, но и для многих других подобных классов (наиболее распространенными являются политики).
Ответ 3
Публичные модели наследования IS-A.
Непубличные модели наследования IS-IMPLEMENTED-IN-TERMS-OF.
Модели сдерживания HAS-A, что эквивалентно IS-IMPLEMENTED-IN-TERMS-OF.
Sutter по теме. Он объясняет, когда вы выбираете непубличное наследование поверх сдерживания для деталей реализации.
Ответ 4
Частное наследование в основном используется по неправильной причине. Люди используют его для IS-IMPLEMENTED-IN-TERMS-OF, как указано в более раннем ответе, но по моему опыту всегда более чисто, чтобы сохранить копию, а не наследовать от класса. Другой более ранний ответ, один о CBigArray, является прекрасным примером этого анти-шаблона.
Я понимаю, что могут быть случаи, когда has-a не работает из-за чрезмерного усердного использования "защищенного", но лучше исправить сломанный класс, чем сломать новый класс.
Ответ 5
Например, если вы хотите повторно использовать реализацию, но не интерфейс класса И переопределить его виртуальные функции.
Ответ 6
Я использовал как частную, так и защищенную наследование в какой-то момент.
Частное наследование полезно, когда вы хотите, чтобы что-то имело поведение базового класса, а затем можно переопределить эту функциональность, но вы не хотите, чтобы весь мир знал об этом и использовал его. Вы по-прежнему можете использовать интерфейс частного класса, если функция возвращает этот интерфейс. Это также полезно, когда вы можете заставить вещи регистрироваться для прослушивания обратных вызовов, поскольку они могут регистрироваться с использованием частного интерфейса.
Защищенное наследование особенно полезно, когда у вас есть базовый класс, который извлекает полезную функциональность из другого класса, но вы хотите, чтобы его производные классы могли его использовать.
Ответ 7
Я однажды применил эти структуры данных как классы:
- Связанный список
- Общий массив (аннотация)
- Простой массив (наследуется от общего массива)
- Большой массив (наследуется от общего массива)
Интерфейс большого массива сделает его похожим на массив, однако на самом деле это был связанный список простых массивов фиксированного размера. Поэтому я объявил следующее:
template <typename T>
class CBigArray : public IArray, private CLnkList {
// ...