Наследование и полиморфизм в C++
Наследование и полиморфизм в C++: базовые и производные классы, override, виртуальные функции и динамическое связывание.
Наследование позволяет создавать новый класс на основе существующего, переиспользуя его код. Полиморфизм позволяет работать с объектами разных классов через единый интерфейс.
Наследование
Производный класс (дочерний) наследует поля и методы базового через :.
#include <iostream>
#include <string>
class Animal {
public:
std::string name;
Animal(std::string n) : name(n) {}
void eat() {
std::cout << name << " ест\n";
}
};
class Cat : public Animal {
public:
Cat(std::string n) : Animal(n) {} // вызов конструктора базового класса
void meow() {
std::cout << name << " говорит: Мяу!\n";
}
};
int main() {
Cat c("Барсик");
c.eat(); // унаследованный метод
c.meow(); // собственный метод
return 0;
}
Вывод:
Барсик ест Барсик говорит: Мяу!
Виртуальные функции и полиморфизм
Слово virtual в базовом классе говорит: при вызове через указатель/ссылку — смотри на реальный тип объекта. override в производном классе подтверждает переопределение.
#include <iostream>
class Shape {
public:
virtual void draw() const { // виртуальная функция
std::cout << "Фигура\n";
}
virtual ~Shape() {} // виртуальный деструктор — важно!
};
class Circle : public Shape {
public:
void draw() const override { // переопределяем
std::cout << "Круг\n";
}
};
class Square : public Shape {
public:
void draw() const override {
std::cout << "Квадрат\n";
}
};
int main() {
Shape* shapes[3] = {new Shape(), new Circle(), new Square()};
for (Shape* s : shapes) {
s->draw(); // вызывается нужная версия — динамическое связывание
}
for (Shape* s : shapes) delete s;
return 0;
}
Вывод:
Фигура Круг Квадрат
Всегда объявляйте деструктор базового класса
virtual, иначе при удалении объекта через указатель на базовый класс вызовется только деструктор базового — утечка ресурсов дочернего.
Абстрактный класс
Класс с чисто виртуальной функцией (= 0) нельзя создать напрямую — он задаёт интерфейс для производных.
class AbstractAnimal {
public:
virtual void sound() = 0; // чисто виртуальная функция
virtual ~AbstractAnimal() {}
};
class Dog : public AbstractAnimal {
public:
void sound() override { std::cout << "Гав!\n"; }
};
Коротко
- Наследование:
class Child : public Base— дочерний класс получает всёpublicот базового. virtual+override— динамическое связывание: вызывается метод реального типа объекта.- Всегда делайте деструктор базового класса
virtual. - Чисто виртуальная функция
= 0делает класс абстрактным — объект не создашь.
Проверьте себя
1. Зачем нужно слово virtual у функции?
AДля оптимизации
BЧтобы вызов через указатель базового класса находил метод реального типа
CДля шаблонного программирования
DДля передачи по ссылке
2. Почему деструктор базового класса должен быть virtual?
AЭто требование стандарта
BИначе при delete через указатель на базу вызовется только базовый деструктор
CЧтобы ускорить удаление
DБез virtual деструктор не компилируется
3. Что делает ключевое слово override?
AЗапрещает дальнейшее наследование
BПодтверждает, что метод переопределяет виртуальный метод базового класса
CСоздаёт виртуальную функцию
DВызывает базовую версию метода