Предполагается, что вы уже знакомы с понятиями класс, объект, функция, переменная и т.д. В статье пойдет речь о понятии наследования со всеми вытекающими. Если приведенный в конце код вам понятен, не тратьте на нее время. Допустим, создается стратегическая игра и поставлена задача создания всяческих боевых и не боевых единиц. Начнем с создания фермера, но так как фермеров будет много и все разные, но с одинаковыми параметрами, характеризующими определенного фермера, логично будет создать его класс, а самих фермеров задавать как объекты этого класса: #include <iostream>
using namespace std;
class Unit //базовый класс фермера
{
protected:
int health;
public:
void showHealth() { cout << "Unit health: " << health << endl; };
Unit(): health(10) { } //конструктор по умолчанию
Unit(int a): health(a) { } //конструктор с параметром
};
С этим классом должно быть все понятно. Его объекты будут характеризоваться одним параметром "health" (здоровье) и есть один метод, позволяющий вывести значение этого параметра на экран. Можно создавать фермеров: int main()
{
Unit bob;
bob.showHealth();
Unit jack(30);
jack.showHealth();
return 0;
}
На экран выведется Unit health: 10 и Unit health: 30. Ничего нового пока. Теперь создадим воинов. Кроме того, что у них есть здоровье, как у фермеров, они могут наносить урон (damage). Можно было бы создать отдельный класс для воинов с параметрами healthи damage, но гораздо проще создать класс воинов, имеющий параметр healthкласса фермеров и свой параметр damage. Это и есть наследование: class Soldier : public Unit
{
private:
int damage;
public:
void showDamage(){cout << "Sodier damage: " << damage << endl;}
//для базового класса используется конструктор по умолчанию
Soldier():damage(20) { }
Soldier(int a):damage(a) { } //также health=10
//используется конструктор Unit(int a), т.е. health=a
Soldier(int a, int b):Unit(a),damage(b) { }
};
То есть теперь, создавая объект класса Soldier, можно использовать как его поля, так и поля базового класса Unit: int main()
{
Soldier hercules(30);
hercules.showHealth();
hercules.showDamage();
Soldier achilles(40,50);
achilles.showHealth();
achilles.showDamage();
return 0;
}
В результате у Геракла теперь 10 healthи 30 damage, а у Ахиллеса 40 healthи 50 damage. Сейчас я поясню несколько моментов, которые могли вызвать затруднения, но сначала необходимо изменить метод доступа поля health класса Unitс privateна protected, чтобы код скомпилировался. protectedотличается отprivateтем, что protectedне запрещает доступ к полям классам-наследникам. Теперь о моментах: -при объявлении класса-наследника необходимо представить базовый класс: class Soldier : public Unit -при объявлении базового класса необходимо предоставить модификатор доступа (public, private, protected) перед именем базового класса. Модификаторpublic позволяет классу-наследнику наследовать поля базового класса так, как они объявлены в базовом классе, protectedпреобразует public в protected, а модификатор privateнаследует поля базового класса, как private, вне зависимости от того, как они заданы в базовом классе. Следует отметить, что наследование ни как не изменяет базовый класс -в представленном наследнике, для примера, введено несколько конструкторов. При создании объекта класса Soldier, в случае первых двух конструкторов для инициализации полей базового класса Unitбудет использоваться его конструктор по умолчанию. Третий же конструктор Soldier вызывает конструктор с параметром базового Unit
Хорошо, теперь аналогичным образом создадим лошадей, которые в отличии от фермеров, будут двигаться, т.е. будут иметь параметр speed: class Horse : public Unit
{
private:
int speed;
public:
void showSpeed(){cout << "Horse speed: " << speed << endl;}
Horse():speed(20) { }
};
Все лошади будут иметь 10 healthи 20 speed. А теперь создадим всадников, которые будут иметь характеристики как фермеров(здоровье) и солдат (damage), так и лошадей (speed): class Horseman : public Horse, public Soldier { };
И все бы ничего, но наследуя и Horseи Soldier, Horsemanнаследует 2 копии Unit. Решить эту проблему можно использовав ключевое слово virtualперед объявлениями о наследовании класса Unit(не путайте с виртуальными функциями): class Soldier : virtual public Unit
class Horse : virtual public Unit
Все, класс всадников создан! При создании объекта класса Horsemanбудут использоваться конструкторы по умолчанию базовых классов. Допустим, что солдат тоже может двигаться (имеет параметр speed): class Soldier : virtual public Unit
{
protected:
int speed; //имеет параметр скорости
public:
//имеет метод вывода значения
//скорости на экран
void showSpeed(){cout << "Soldier speed: " << speed << endl;}
Soldier():speed(3){}
};
class Horse : virtual public Unit
{
protected:
int speed; //имеет параметр скорости как и Soldier
public:
//имеет метод вывода значения
//скорости на экран как и Soldier
void showSpeed() { cout << "Horse speed: " << speed << endl; }
Horse():speed(20){ }
};
class Horseman : public Horse, public Soldier{}
int main()
{
Horseman lancelot;
//метод какого из базовых классов использовать?
//какое значение speed?
lancelot.showSpeed();
return 0;
}
Неоднозначность можно решить дважды использовав оператор разрешения контекста: lancelot.::Horse::showSpeed();
Но правильнее было бы переназначить поле в классе Horseman (код целиком): #include <iostream>
using namespace std;
class Unit
{
protected:
int health;
public:
void setHealth(int a) { health = a; }
void showHealth(){ cout << "Unit health: " << health << endl; };
Unit(): health(10) { }
Unit(int a): health(a) { }
};
class Soldier : virtual public Unit
{
protected:
int damage;
int speed;
public:
void showDamage() { cout << "Sodier damage: " << damage << endl; }
void showSpeed() { cout << "Soldier speed: " << speed << endl; }
Soldier():damage(20), speed(3) { }
};
class Horse : virtual public Unit
{
protected:
int speed;
public:
void showSpeed(){cout << "Horse speed: " << speed << endl;}
Horse():speed(20) { }
};
class Horseman : public Horse, public Soldier
{
private:
int speed;
public:
void showSpeed(){cout << "Horseman speed: " << speed << endl;}
Horseman():speed(Horse::speed){}
};
int main()
{
Horseman lancelot;
lancelot.showHealth();
lancelot.showDamage();
lancelot.showSpeed();
return 0;
}
|