Материал: Laboratornaya_rabota_9)_условие

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

ЛАБОРАТОРНАЯ РАБОТА №9

НАСЛЕДОВАНИЕ И ДИНАМИЧЕСКИЙ ПОЛИМОРФИЗМ

1. Родительские и дочерние классы

Класс, который наследуется другим классом, называется базовым или родительским. Класс, выполняющий наследование, называется производным или дочерним. При этом как у одного базового класса может быть несколько родительских (множественное наследование), так и у одного родительского несколько базовых.

Спецификаторы доступа при наследовании

В С++ члены класса классифицируются в соответствии с правами доступа на следующие три категории: открытые (public), закрытые (private) и защищенные (protected).

От того, с каким спецификатором доступа объявляется наследование базового класса, зависит статус доступа к членам производного класса. Общая форма наследования классов имеет следующий вид:

class имя_производного_класса: доступ имя_базового_класса

{

// код

};

Здесь доступ определяет, каким способом наследуется базовый класс. Спецификатор доступа может принимать одно из трех значений: private, protected, public. Если он не указан, то: подразумевается public для производных классов объявленных через struct, и private – для объявленных через class.

Если спецификатор принимает значение public, то все открытые и защищенные члены базового класса становятся соответственно открытыми и защищенными членами производного класса.

Если указан protected, то все открытые и защищенные члены базового класса становятся защищенными членами производного класса.

Если указан private, то все открытые и защищенные члены базового класса становятся закрытыми членами производного класса.

Деструкторы производных классов

В деструкторе производного класса компилятор автоматически генерирует вызовы деструкторов его базовых классов.

Для удаления объекта производного класса следует сделать деструктор в базовых классах виртуальным с целью корректного вызова деструкторов при использовании оператора delete.

Передача параметров в базовый класс

Когда базовый класс имеет конструктор с аргументами, производные классы должны передавать базовому классу необходимые аргументы. Для этого используется расширенная форма конструкторов:

порожденный_конструктор(список_аргументов):

базовый1(список_аргументов1),

…,

базовыйN(список_аргументовN)

{

// код

}

Здесь под базовый1, …, базовыйN обозначены имена базовых классов. С помощью двоеточия конструктор производного класса отделяется от списка конструкторов базового класса. Списки аргументов, передаваемых в конструкторы базовых классов, могут состоять из констант, глобальных переменных или параметров конструктора производного класса.

Если конструкторы производных классов не вызываются в явном виде (как в примере выше), вместо них из базовых классов автоматически вызываются конструкторы без параметров. Кроме того, конструкторы без параметров автоматически вызываются при инициализации вместо указанных конструкторов с параметрами в случае, если имеет место виртуальное наследование (см. ниже).

Указатели и ссылки на производные классы

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

Виртуальные функции

Виртуальная функция – это функция, объявленная с ключевым словом virtual в базовом классе и переопределенная в производных классах.

Это означает, что при использовании объекта производного класса с помощью указателя или ссылки на него как на объект базового класса, виртуальная функция вызывается из того класса, посредством которого этот объект был создан. То есть, в одном и том же участке кода для разных объектов могут вызываться разные версии одной и той же виртуальной функции. Класс, содержащий хотя бы одну виртуальную функцию, называется полиморфным классом.

Для виртуальных функций существуют следующие правила:

– виртуальную функцию нельзя объявлять как static;

– спецификатор virtual необязателен (но желателен) при переопределении функции в производном классе.

2. Пример использования виртуальных функций

#include <iostream>

#include <typeinfo>

using namespace std;

class creature

{

private:

string title;

double mass;

public:

creature() :mass(0.0) {} // конструктор без параметров

// конструктор копирования

creature(const creature& obj):title(obj.title), mass(obj.mass){}

// конструктор с параметрами

creature(const string& _title, double _mass) : title(_title), mass(_mass) {}

// деструктор (объявлен как виртуальный)

virtual ~creature() { cout << "creature deleted" << endl << endl; }

protected:

// защищенная виртуальная функция для вывода информации

// об объекте

// не должна использоваться вне данного класса и его

//производных

virtual void _print() const

{

cout << "title: " << title << endl;

cout << ", mass: " << mass << endl;

}

public:

// открытая функция для вывода информации об объекте

void print() const

{

// выводит название класса, к которому принадлежит

// объект, для которого она вызвана,

cout << typeid(*this).name() << ": (";

// и вызывает виртуальную функцию

// т.к. функция _print виртуальная, вызываться она

// будет не только из текущего класса, но и из производных,

// в зависимости от того, для какого объекта

// осуществляется вызов

_print();

cout << ")" << endl;

}

};

// класс animal наследуется от класса creature

class animal : public creature

{

private:

double speed; // m/s

public:

// конструкторы класса animal осуществляют вызовы

// конструкторов

// базового класса (creature)

animal(): creature(), speed(0.0) {}

animal(const animal& obj): creature(obj), speed(obj.speed) {}

animal(const string& _title, double _mass, double _speed): creature(_title, _mass), speed(_speed){}

~animal() { cout << "animal deleted" << endl; }

protected:

// виртуальная функция _print переопределяется в

// производном классе

void _print() const

{

creature::_print();

cout << ", speed: " << speed;

}

};

// класс bird наследуется от класса animal

class bird : public animal

{

private:

double topfly; // km

public:

bird(): animal(), topfly(0.0) {}

bird(const bird& obj): animal(obj), topfly(obj.topfly){}

bird(const string& _title, double _mass, double _speed, double _topfly):

animal (_title, _mass, _speed), topfly(_topfly) {}

~bird() { cout << "bird deleted" << endl; }

protected:

// виртуальная функция _print переопределяется

// теперь уже в классе bird

void _print() const

{

animal::_print();

cout << ", topfly: " << topfly;

}

};

// класс fish наследуется от класса animal

class fish : public animal

{

private:

double maxdeep; //km

public:

fish(): animal(), maxdeep(0.0) {}

fish(const fish& obj): animal(obj), maxdeep(obj.maxdeep) {}

fish(const string& _title, double _mass, double _speed, double _maxdeep):

animal(_title, _mass, _speed), maxdeep(_maxdeep) {}

~fish() { cout << "fish deleted" << endl; }

protected:

// виртуальная функция _print переопределяется

// в классе fish

void _print() const

{

animal::_print();

cout << ", maxdeep: " << maxdeep;

}

};

int main()

{

setlocale(0, "Rus");

// создание объектов и вывод информации о них

animal("Паук", 0.003, 0.05).print();

bird("Ворона", 0.3, 10, 0.1).print();

fish("Рыба-молот", 150, 5, 0.5).print();

cout << endl;

system("pause");

return 0;

}

3. Чисто виртуальные функции и абстрактные классы

Заметим, что не в любом случае для базового класса можно определить поведение той или иной функции. Примеры этого можно найти в реальном мире, в частности, все животные в том или ином виде могут перемещаться: рыбы плавают, звери бегает, змеи ползают, птицы летают (упрощено для примера). Однако как именно перемещается некое абстрактное животное, сказать нельзя.

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

virtual void fun() = 0;

Класс, в котором определена хотя бы одна чисто виртуальная функция, называется абстрактным. Следует отметить, что создать нельзя объект от абстрактного класса непосредственно. Объекты можно создавать только от тех его производных классов, в которых все чисто виртуальные функции переопределены.

Дополним приведенный выше пример:

#include <iostream>

#include <typeinfo>

using namespace std;

class creature

{

private:

string title;

double mass; //kg

protected:

// конструкторы вынесены в protected, т.к. нет необходимости

// создавать

// объекты непосредственно этого класса нет, однако, эти

// конструкторы должны быть доступны из производных

// классов

// аналогично вынесен деструктор

creature() :mass(0.0) {} // конструктор без параметров

// конструктор копирования

creature(const creature& obj) :title(obj.title), mass(obj.mass) {}

// конструктор с параметрами

creature(const string& _title, double _mass) : title(_title), mass(_mass) {}

// деструктор (объявлен как виртуальный)

virtual ~creature() { cout << "creature deleted" << endl << endl; }

// защищенная виртуальная функция для вывода информации

// об объекте

// не должна использоваться вне данного класса и его

// производных

virtual void _print() const

{

cout << "title: " << title << endl;

cout << ", mass: " << mass << endl;

}

public:

// открытая функция для вывода информации об объекте

void print() const

{

// выводит название класса, к которому принадлежит

// объект, для которого она вызвана,

cout << typeid(*this).name() << ": (";

// и вызывает виртуальную функцию

// т.к. функция _print виртуальная, вызываться она

// будет не только из текущего класса, но и из производных,

// в зависимости от того, для какого объекта

// осуществляется вызов

_print();

cout << ")" << endl;

}

double get_mass() const { return mass; }

void set_mass(double new_mass) { mass = new_mass; }

};

// класс animal наследуется от класса creature

class animal : public creature

{

private:

double speed; // m/s

protected:

// конструкторы класса animal осуществляют вызовы

// конструкторов

// базового класса (creature)

animal() : creature(), speed(0.0) {}

animal(const animal& obj) : creature(obj), speed(obj.speed) {}

animal(const string& _title, double _mass, double _speed) : creature(_title, _mass), speed(_speed) {}

~animal() { cout << "animal deleted" << endl; }

// виртуальная функция _print переопределяется в

// производном классе

void _print() const

{

creature::_print();

cout << ", speed: " << speed;

}

public:

double get_speed() const { return speed; }

};

// класс bird наследуется от класса animal

class bird : public animal

{

private:

double topfly; // km

Источник: https://studfile.net/preview/16672684/