Наследование
Птица тоже животное: у неё есть всё, что есть у любого животного, и кое-что своё. Если описать Bird отдельным классом, поля животного придётся повторить. Вместо этого Bird объявляют производным от Animal:
#include <iostream>
struct Animal {
int legs = 4;
};
struct Bird : public Animal {
bool flies = true;
};
int main() {
Bird b;
b.legs = 2;
std::cout << b.legs << ' ' << b.flies << '\n'; // 2 1
}Базовый и производный класс
Здесь Animal — базовый класс, а Bird — производный класс. Объект Bird содержит внутри целый объект Animal, поэтому у b есть и поле legs, и собственное поле flies. Поля и методы базового класса переходят к производному, а конструкторы не переходят. Если у Animal есть конструктор Animal(int legs), производный класс вызывает его в своём списке инициализации: Bird() : Animal(2) {}.
Слово public перед Animal сохраняет доступ: открытые поля и методы базового класса остаются открытыми и у производного. Закрытые поля базового класса тоже лежат в объекте, но методы производного класса обращаться к ним не могут. Для полей, которые нужны производным классам, но закрыты от остального кода, есть метка protected:. Метод класса Bird может менять такое поле Animal, а функция main — нет.
Ссылки, указатели и копии
Производный объект можно передать туда, где ждут базовый: ссылка Bird& неявно преобразуется в Animal&, а указатель Bird* — в Animal*. Поэтому одна функция с параметром const Animal& подходит для любого класса, производного от Animal. Через такую ссылку или указатель доступны только поля и методы Animal. К полю по указателю обращаются через стрелку: ptr->legs — то же, что (*ptr).legs.
С копией всё иначе. Если записать производный объект в переменную базового типа, создаётся новый объект Animal, и в него копируется только часть Animal. Это срезка: поле flies в копию не попадает, а сама копия живёт отдельно от b.
Bird b;
Animal& ref = b;
Animal copy = b;
b.legs = 2;
std::cout << ref.legs << ' ' << copy.legs << '\n'; // 2 4ref обозначает сам объект b и видит новое значение, а copy сохранила прежнее. Строка copy.flies не скомпилируется: у объекта Animal такого поля нет.
Частая ошибка
Производный класс объявили через class и не написали public:
class Animal {
public:
int legs = 4;
};
class Bird : Animal {};
int main() {
Bird b;
return b.legs; // ✗
}error: 'legs' is a private member of 'Animal'У class наследование по умолчанию закрытое: открытые поля Animal становятся закрытыми в Bird. Напишите class Bird : public Animal. У struct наследование по умолчанию открытое, но public пишут и там, чтобы не гадать.
Главное
- Производный класс содержит целый объект базового и добавляет свои поля и методы.
- Ссылка или указатель на производный объект неявно преобразуются в ссылку или указатель на базовый.
- Копия в переменную базового типа срезается: в неё попадает только базовая часть.
- Пишите
publicперед базовым классом, иначе уclassнаследование закрытое.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.