- Создавать производный класс и вызывать конструктор базового класса
- Переопределять методы с помощью
virtualиoverride - Объяснять, как полиморфизм работает через указатели и ссылки на базовый класс
- Создавать абстрактный класс с чисто виртуальными функциями
В игре есть воин, маг и лучник. У всех есть имя и здоровье, все атакуют — но каждый по-своему. Вместо того чтобы повторять одинаковый код в трёх классах, общее можно один раз написать в базовом классе, а различия вынести в производные классы. Это наследование. А полиморфизм позволяет обращаться со всеми одинаково: ты говоришь «атакуй!», и каждый персонаж атакует по-своему.
Наследование
Запись class Warrior : public Character означает «Warrior наследуется от Character». Производный класс получает все члены базового и может добавить свои. protected-члены базового класса закрыты для внешнего кода, но видны производным классам. Конструктор производного класса вызывает конструктор базового в списке инициализации.
#include <iostream>
#include <string>
class Character {
public:
Character(const std::string& name, int health)
: name_(name), health_(health) {}
void status() const {
std::cout << name_ << " has " << health_ << " HP\n";
}
protected:
std::string name_;
int health_;
};
class Warrior : public Character {
public:
Warrior(const std::string& name) : Character(name, 150) {}
void shieldUp() {
health_ += 20;
std::cout << name_ << " raises a shield\n";
}
};
int main() {
Warrior w("Elvin");
w.status();
w.shieldUp();
w.status();
return 0;
}Elvin has 150 HP Elvin raises a shield Elvin has 170 HP
status() достаётся от базового класса в готовом виде, а shieldUp() есть только у Warrior.Виртуальные функции
К производному объекту можно обращаться через ссылку или указатель на базовый класс: каждый воин — это ещё и персонаж. Но какой метод тогда вызовется? У обычного метода — версия базового класса, потому что выбор делается при компиляции по типу ссылки. Если метод объявлен virtual, выбор делается во время работы программы по настоящему типу объекта.
#include <iostream>
class Base {
public:
virtual ~Base() = default;
void hello() const { std::cout << "Base::hello\n"; }
virtual void greet() const { std::cout << "Base::greet\n"; }
};
class Derived : public Base {
public:
void hello() const { std::cout << "Derived::hello\n"; }
void greet() const override { std::cout << "Derived::greet\n"; }
};
int main() {
Derived d;
const Base& ref = d;
ref.hello();
ref.greet();
return 0;
}Base::hello Derived::greet
hello не виртуальный — решает тип ссылки (Base); greet виртуальный — решает настоящий тип объекта (Derived).Абстрактные классы и полиморфизм
Иногда базовый класс не знает, как выполнить метод: «площадь фигуры» в общем виде не вычислить. Такой метод объявляют чисто виртуальным с помощью = 0. Класс хотя бы с одним чисто виртуальным методом — абстрактный: создать его объект нельзя, от него можно только наследоваться. Зато разные фигуры можно хранить в одном векторе и работать с ними одинаково:
#include <iostream>
#include <memory>
#include <vector>
class Shape {
public:
virtual ~Shape() = default;
virtual int area() const = 0;
};
class Rect : public Shape {
public:
Rect(int w, int h) : w_(w), h_(h) {}
int area() const override { return w_ * h_; }
private:
int w_, h_;
};
class Triangle : public Shape {
public:
Triangle(int b, int h) : b_(b), h_(h) {}
int area() const override { return b_ * h_ / 2; }
private:
int b_, h_;
};
int main() {
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Rect>(4, 3));
shapes.push_back(std::make_unique<Triangle>(6, 5));
int total = 0;
for (const auto& s : shapes) {
std::cout << "Area: " << s->area() << '\n';
total += s->area();
}
std::cout << "Total: " << total << '\n';
return 0;
}Area: 12 Area: 15 Total: 27
Shape, но каждый объект вызывает свой area() — это и есть полиморфизм.| Термин | Значение |
|---|---|
| базовый класс | класс с общей частью (Shape) |
| производный класс | класс, унаследованный от базового и расширяющий его (Rect) |
virtual | метод выбирается по настоящему типу объекта |
override | просит компилятор проверить, что виртуальный метод действительно переопределён |
= 0 | чисто виртуальный метод; класс становится абстрактным |
Главное
class B : public A—Bнаследуется отAи получает его члены.protected-члены видны производным классам, но не внешнему коду.virtual-метод, вызванный через ссылку или указатель на базовый класс, выбирается по настоящему типу объекта.overrideловит ошибки при компиляции; у полиморфного базового класса деструктор должен бытьvirtual.- Чисто виртуальный метод (
= 0) делает класс абстрактным: объекты создать нельзя.
Проверь себя
Вопросов: 10. Каждый правильный ответ приносит XP.
Archer от Character?