Топ контрибуторов
loading
loading
Знаете ли Вы, что

В разделе "Статьи" можно найти обучающие статьи по информационным технологиям, а также узнать о новостях сервиса Quizful.

Лента обновлений
ссылка Oct 22 23:24
Комментарий от alexcei88:
В вопросе переменная s даже не выводиться, а выводитьс...
ссылка Oct 22 17:46
Комментарий от AlexFurm:
Это UB, так можно вызывать только статические функции ч...
ссылка Oct 22 17:43
Комментарий от AlexFurm:
Любые битовые операции с signed это UB
ссылка Oct 21 20:30
Комментарий от yoori:
Любой вариант скомпилируется если компилировать не в конеч...
ссылка Oct 21 16:53
Добавлен вопрос в тест QA (Quality Assurance)
Статистика

Тестов: 153, вопросов: 8596. Пройдено: 443368 / 2177510.

Доступно про наследование в С++

head tail Статья
категория
C++
дата05.12.2012
авторVS_Revan
голосов0

Предполагается, что вы уже знакомы с понятиями класс, объект, функция, переменная и т.д. В статье пойдет речь о понятии наследования со всеми вытекающими. Если приведенный в конце код вам понятен, не тратьте на нее время.

Допустим, создается стратегическая игра и поставлена задача создания всяческих боевых и не боевых единиц. Начнем с создания фермера, но так как фермеров будет много и все разные, но с одинаковыми параметрами, характеризующими определенного фермера, логично будет создать его класс, а самих фермеров задавать как объекты этого класса:


#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;
}

Если Вам понравилась статья, проголосуйте за нее

Голосов: 0  loading...