В чем разница между public, protected и private наследованием в C++?
Ключевая идея: наследование определяет уровень доступа к унаследованным членам
Разница между public, protected и private наследованием в C++ — это не про то, что члены базового класса "исчезают", а про то, как меняется их видимость для внешнего мира и для классов-наследников. Каждый спецификатор наследования "понижает" уровень доступа унаследованных членов до указанного уровня (или оставляет как есть).
Давайте разберем на примере базового класса:
class Base {
public:
int pub = 1;
protected:
int prot = 2;
private:
int priv = 3;
};
Теперь создадим три разных класса наследника и посмотрим, что произойдет с каждым членом.
1. Public наследование: "как есть"
class DerivedPublic : public Base {
// pub остается public
// prot остается protected
// priv остается недоступным (остается private в Base)
};
- pub — доступен отовсюду: внутри DerivedPublic, из объектов этого класса, из классов-наследников.
- prot — доступен внутри DerivedPublic и его наследников, но не извне (через объект).
- priv — недоступен даже внутри DerivedPublic, так как private члены базового класса никогда не наследуются.
Это самый распространенный вид наследования — моделирует отношение "является" (is-a).
2. Protected наследование: "понижаем до protected"
class DerivedProtected : protected Base {
// pub становится protected
// prot остается protected
// priv остается недоступным
};
- pub — теперь защищен: доступен внутри DerivedProtected и его наследников, но не извне.
- prot — остается protected (уровень не понижается, так как он уже protected).
- priv — все еще недоступен.
Используется, когда вы хотите скрыть от внешнего мира тот факт, что класс наследует от Base, но при этом хотите, чтобы наследники могли пользоваться унаследованными членами.
3. Private наследование: "понижаем до private"
class DerivedPrivate : private Base {
// pub становится private
// prot становится private
// priv остается недоступным
};
- pub и prot — теперь приватные: доступны только внутри самого DerivedPrivate, но не извне и не в классах-наследниках.
- priv — все еще недоступен.
Это моделирует отношение "реализовано через" (implemented-in-terms-of). Внешний мир не должен знать, что DerivedPrivate наследует от Base — это деталь реализации.
Проверка на практике
int main() {
DerivedPublic dp;
dp.pub = 10; // OK: pub унаследован как public
// dp.prot = 20; // Ошибка: prot защищен
DerivedProtected dprot;
// dprot.pub = 10; // Ошибка: pub стал protected, недоступен извне
DerivedPrivate dpriv;
// dpriv.pub = 10; // Ошибка: pub стал private
return 0;
}
Важное замечание: private члены базового класса
Независимо от типа наследования, private члены базового класса никогда не наследуются. Они существуют в объекте (занимают память), но напрямую обратиться к ним нельзя — только через public/protected методы базового класса.
Итоговая таблица
| Тип наследования | Что происходит с public членами Base | Что происходит с protected членами Base | private члены Base |
|---|---|---|---|
| public | остаются public | остаются protected | недоступны |
| protected | становятся protected | остаются protected | недоступны |
| private | становятся private | становятся private | недоступны |
Запомните правило: спецификатор наследования — это "потолок" доступа. Унаследованный член не может иметь более высокий уровень доступа, чем указанный при наследовании.
<<Дополнение: когда что использовать на практике
Коллега выше отлично описал механику. Я добавлю практические рекомендации, когда какой тип наследования выбирать.
Public наследование (90% случаев)
Используйте всегда, если класс-наследник логически является разновидностью базового класса. Например, class Dog : public Animal. Внешний код должен иметь доступ к интерфейсу Animal через объект Dog. Это основное отношение "is-a".
Protected наследование (редко)
Применяется, когда вы хотите скрыть наследование от внешнего мира, но разрешить его своим наследникам. Например, если вы пишете библиотеку и не хотите, чтобы пользователи знали, что ваш класс наследует от какого-то внутреннего класса, но при этом ваши собственные классы-наследники могут использовать его члены. Встречается в паттерне "Template Method".
Private наследование (иногда)
Используется для композиции на уровне наследования, когда нужно получить доступ к protected членам или переопределить виртуальные функции, но не хотите, чтобы внешний код знал о наследовании. Например:
class Engine {
public:
void start();
protected:
int rpm;
};
class Car : private Engine {
public:
void drive() { start(); } // используем Engine, но не показываем это
};
Внешний код не сможет написать Car c; c.start(); — наследование скрыто. Это альтернатива композиции (члену класса), когда нужно переопределить виртуальные методы.
Золотое правило: если сомневаетесь — используйте public. Private и protected наследование — это инструменты для специфических случаев, которые новичку встречаются редко.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги
Как сохранить изображения в базе данных SQLite? Полный гайд для разработчиков на Python