В сегодняшнем уроке мы будем говорить о наследовании. Лучше всего наследование изучать на конкретных примерах.
Итак, наследование. Данный вид взаимоотношения классов применяется в разных ситуациях.
Начнём с примера:
PHP код:
class infantry
{
private:
int health;
public:
ind damage;
infantry() : health(100),damage(10) // конструктор по умолчанию
{}
infantry(int h, int d) : health(h), damage(d) // конструктор с двумя аргументами
{}
void set_health (int h) // записать значение в поле health
{
health = h;
}
int get_health () // получить значение из поля health
{
return health;
}
};
Здесь у нас определён класс infantry (пехота). В классе два поля: health (здоровье) и damage (урон противнику). Переменная-член health расположена в блоке private. Доступ к данному полю возможен только через функции set_health() и get_health(). Для damage тоже нужно использовать похожие функции, но для сокращения кода я просто объявил damage в блоке public.
Кроме того я добавил два конструктора: по умолчанию и с двумя аргументами.
С помощью наследования можно исправить или улучшить существующий класс.
Допустим, мы пишем игру уже довольно давно: несколько дней или даже несколько месяцев. И на каком-то этапе понадобилось включить пехотинцев с гранатами. Причём кроме гранат, данный вид пехотинцев полностью похож на объекты класса infantry. К тому же, обычно ситуация усугу***ется ещё и тем, что в классе не два поля и две функции, а с десяток и того и другого.
Единственное изменение, которое нам необходимо сделать в классе infantry: ключевое слово private заменить на protected:
PHP код:
class infantry
{
protected:
int health;
public:
// остальной код остался без изменений
};
Теперь создаём новый класс:
PHP код:
class grenadier : public infantry
{
public:
int grenades;
};
Поле grenades я объявил в блоке public, чтобы не писать функции доступа.
Данный класс обладает всеми свойствами класса infantry и вдобавок у него есть поле grenades. При этом класс infantry - базовый класс, а класс grenadier - производный.
В заголовке класса grenadier после двоеточия стоит ключевое слово public и имя базового класса infantry. public задаёт общее наследование. Существует ещё частное (private), но мы пока не будем обсуждать такие тонкости.
Спецификатор доступа protected
Спецификаторы доступа public (общий) и private (частный) мы обсуждали при изучении классов. Существует ещё один спецификатор доступа - protected (защищённый).
Спецификатор доступа protected аналогичен private, только он позволяет получить доступ к защищённым данным из производного класса. Когда в программе вы будете применять наследование, то вместо спецификатора private всегда используйте protected.
С помощью наследования удобно строить иерархию классов. При этом более общие и абстрактные классы - базовые, а более специализированные - производные.
Пусть общим классом у нас будет - солдат. В данном классе можно задать только движение. Затем можно создать более специализированные классы, обладающие конкретными свойствами: стрелок, снайпер, пулемётчик. При этом для производных классов уже не надо писать функции движения, они их унаследуют от базового.
Множественное наследование
Для пояснения данной темы нам понадобится ещё один класс
PHP код:
class lorry
{
public:
int fuel;
lorry(): fuel(100)
{}
lorry(int f) : fuel(f)
{}
};
Здесь объявлен класс lorry (грузовик) с одним полем fuel (топливо) и двумя конструкторами.
Объединив два класса infantry и lorry, мы можем получить новый класс:
PHP код:
class mech_inf : public infantry, public lorry
{
public:
mech_inf() : infantry(), lorry() // обратите внимание на списки инициализации
{}
mech_inf(int h, int d, int f) : infantry(h,d), lorry(f)
{}
};
В класс mech_inf (mechanized infantry - мотопехота) мы не добавили ничего нового. Мы просто в заголовке класса mech_inf указали, что он является производным от двух других классов: infantry и lorry. В классе объявляются только два конструктора. Обратите внимание, что в списке инициализации конструктора mech_inf мы вызываем конструкторы infantry() и lorry().
Теперь можно создать объект нового класса.
PHP код:
mech_inf division_1(100, 20, 100);
При этом он обладает свойствами двух базовых классов:
PHP код:
division_1.damage = 10; // damage - член класса infantry
division_1.fuel = 200; // fuel - член класса lorry
Напоследок давайте рассмотрим ещё одну ситуацию. Допустим в классах infantry и lorry есть метод с одним и тем же именем и одними и теми же аргументами. Пусть метод называется move() и в нашем примере у него не будет аргументов.
PHP код:
division_1.move(); // объект класса mech_inf
В данном примере объект не знает, какой из методов вызывать: принадлежащий классу infantry или классу lorry. Чтобы разрешить данную проблему необходимо использовать операцию глобального разрешения:
PHP код:
division_1.infantry::move();
Здесь мы вызываем метод move() объявленный в классе infantry.
Данный случай на первый взгляд не слишком важен. На самом деле, из ситуации, когда у нескольких классов определены методы с одним именем, можно извлечь колоссальную выгоду. Данной теме будет посвящён отдельный урок.