Полиморфизм. Перегрузка операций 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Полиморфизм. Перегрузка операций



 

Полиморфизм в С ++ реализуется через механизм перегрузки. Причем полиморфизм поддерживается в С ++ и во время компиляции, и во время выполнения программы.

 

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

 

Виртуальная функция – это функция, которая объявляется внутри базового класса, переопределяется в производном классе и поддерживает динамический полиморфизм. В базовом классе виртуальная функция определяет вид интерфейса этой функции. Каждое переопределение этой функции в производном классе определяет ее реализацию, связанную со спецификой производного класса. Переопределяемая виртуальная функция должна иметь точно такой же тип параметров, то же их число и такой же тип возвращаемого значения, как и виртуальная функция, объявленная в базовом классе. Этим она отличается от перегружаемой функции. Для того чтобы функция стала виртуальной перед ней нужно поставить слово virtual. Виртуальная функция может вызываться также,  как и любая другая функция-элемент. Однако ключевое значение имеет вызов виртуальной функции через указатель, благодаря чему поддерживается динамический полиморфизм. Если указатель базового класса ссылается на объект производного класса, который содержит виртуальную функцию и для которого виртуальная функция вызывается через этот указатель, то компилятор определяет, какую версию виртуальной функции вызвать, основываясь при этом на типе объекта, на который ссылается указатель. При этом определение конкретной версии виртуальной функции имеет место не в процессе компиляции, а в процессе выполнения программы. Отсюда, если два или более различных класса являются производными от базового, содержащего виртуальную функцию, то, если указатель базового класса ссылается на разные объекты этих производных классов, выполняются различные версии виртуальной функции.

Рассмотрим пример. Пусть имеется базовый класс Base и два производных от него first _ d и second _ d. Структура и взаимоотношения классов придставлены на диаграмме на рисунке 4.2.

Рисунок 4.2 –Диаграмма классов

Программный код, реализующий данную предметную область:

class Base {

public:

virtual void showObjName () { // определение виртуальной функции

cout << "Base\n";

}

};

class first_d: public Base {

public:

void showObjName(){// определение showObjName() для first_d

cout << "First derivation\n";

}

};

class second_d: public Base {

public:

void showObjName(){// определение showObjName () для second_d

cout << "Second derivation\n*";

}

};

int main(){

Base base_obj;

Base *p;

first_d first_obj;

second_d second_obj;

p = &base_obj;

p-> showObjName(); // доступ к showObjName класса Base

p = &first_obj;

p-> showObjName (); // доступ к showObjName класса first_d

p = &second_obj;

p-> showObjName(); // доступ к showObjName класса second_d

return 0;

}

В результате на экран будет выведено:

Base

First derivation

Second derivation

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

class Base {

public:

void showObjName () { // определение виртуальной функции

cout << " Base \ n ";

}

};

class first_d: public Base {

public:

void showObjName () { // определение who() применительно к first_d

cout << "First derivation\n";

}

};

int main(){

Base base_obj;

Base *p;

first_d first_obj;

p = &first_obj;

p -> showObjName (); // вызов через указатель

first _ obj. showObjName (); //вызов по имени объекта

return 0;

}

На экран будет выведено:

Base

First derivation

Если в производном классе виртуальная функция не переопределяется, то тогда используется ее версия из базового класса.

Конструкторы, деструктоы и дружественные функции не могут быть виртуальными, так как они не наследуются.

Чисто виртуальные функции. Во многих случаях невозможно ввести содержательное определение виртуальной функции в базовом классе. Рассмотрим пример. Объявление содержит текст. Оно может быть распечатано на плакате прямоугольной либо круглой формы. Необходимо вывести сведения о плакате, а также вычислить площадь плаката.

Разработаем иерархию классов для работы с рекламным объявлением. На UML-диаграммах виртуальные функции выделяются курсивом.

Рисунок 4.3 – Диаграмма наследования классов.

 

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

Чисто виртуальная функция (pure virtual function) является функцией, которая объявляется в базовом классе, но не имеет в нем определения. Поскольку она не имеет определения, то есть тела в этом базовом классе, то всякий производный класс обязан иметь свою собственную версию ее  определения. Нельзя создать объект, пока в нем не будут определены все методы. Синтаксис объявления чисто виртуальной функции:
virtual тип имя_функции(список параметров) = 0;

Определим базовый класс poster.

class poster {

protected: chartext [100];//текст объявления

public: poster(char* s){

strcpy(text,s);

}

virtual float getArea ()=0;//функция для вычисления площади плаката

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

void print(){

cout<<"Text: "<<text<<". Area: "<< getArea ();

};

};

Функция getArea () объявлена как виртуальная, то есть у нее реализация будет своя реализация в каждом классе-потомке.

Определим два производных класса. Класс circlePoster будет реализовывать «круглый плакат». Он имеет свойство – «радиус круга». Методы: «вычисление площади» и «вывод параметров плаката».

class circlePoster:public poster{

float radius;

public: circlePoster(char*A,float r):poster(A){

radius=r;

};

float getArea(){

return 3.14*radius*radius;

};

void print(){

cout<<"circle poster. Radius "<<radius<<".";

poster::print();

};

};

Класс rectanglePoster будет реализовывать «прямоугольный плакат». Свойства – «длина», «ширина». Методы: «вычисление площади» и «вывод параметров плаката».

class rectanglePoster:public poster{

float width,height;

public: rectanglePoster(char*A,float w,float h):poster(A){

 

height=h;

};

float getArea(){

return width*height;

};

void print(){

cout<<endl<<"Rectangle poster. Size "<<width<<"x"<< height<<".";

poster:: print ();

};

};

Как видим из примера, в функции getA rea() для круглого плаката реализована формула вычисления площади круга, а для прямоугольного плаката реализована формула вычисления площади прямоугольника.

В базовом классе обозначен интерфейс функции getA rea() и она используется в методе print() базового класса. Но конкретная версия функции определяется автоматически, в зависимости от того для какого объекта вызывается функция.

Напишем программный код, чтобы протестировать работу классов:

int main(){

circlePoster A("Adv A",10); // создаем круглый плакат

rectanglePoster B (" Adv B ",10,10);//создаем прямоугольный плакат

A.print();// вывод параметров круглого плаката А

cout<<endl;

B.print();//вывод параметров прямоугольного плаката В

}

Понятие интерфейса. Интерфейс описывает поведение или возможности класса (методы) без привязки к конкретной реализации этого класса. То есть представляет набор заголовков открытых методов, через которые можно взаимодествовать с классом. Каждый производный класс, наследующий интерфейс переопределяет реализацию этих методов. Интерфейсы в C ++ реализованы с использованием абстрактных классов. Класс является абстрактным, если он содержит, хотя бы одну виртуальную функцию. Абстрактные классы не могут использоваться для создания объекта, а служат как интерфейсы. Попытка создать объект абстрактного класса вызывает ошибку компиляции. Можно создать подкласс, наследующий абстрактный класс, при условии что в нем реализованы все виртуальные функции.

В некоторых языках программирования, например в Java, существует специальная конструкция – interface – чисто абстрактный класс – содержит только абстрактные public -функции. Под термином абстрактная функция понимается функция, которая не имеет реализации в заданном классе.

Рисунок 4.4 Интерфейс для предметной области «фигура»

 

#include <iostream>

 using namespace std;

// Базовый класс «фигура»

class shape {

public:

  // чисто виртуальная функция обеспечивает интерфейс

virtual int getArea () = 0;

};

 // Производный класс «прямоугольник»

class rectangle: public shape{

public:

 float width;

 float length;

public: void setWidth(int w) {

width = w;

}

void setLength (int l) {

length = l;

}

float getArea() {

return (width * length);

}

};

// Производный класс «треугольник»

class triangle: public shape{

public:

 float width;

  float height;

public: void setWidth(int w) {

width = w;

}

void setHeight(int h) {

height = h;

}

float getArea() {

return (width * height)/2;

}

};

int main(void){

  rectangle Rect1;

triangle Tri1;

Rect1.setWidth(5);

Rect1.setHeight(7);

// вывод площали объекта

cout << "Total Rectangle area: " << Rect1.getArea() << endl;

Tri1.setWidth(5);

  Tri1.setHeight(7);

  // Print the area of the object.

  cout << "Total Triangle area: " << Tri1.getArea() << endl;

  return 0;

}

В данном примере создан абстрактный класс Shape, реализующий объект «фигура», и в нем определена, как элемент интерфейса, функция getArea (). В двух других производных классах данный интерфейс реализован, причем в каждом классе реализован алгоритм вычисления площади по формулам, свойственным для конкретной фигуры (прямоугольник либо треугольник).

Контрольные вопросы и задания

1.  Что такое виртуальная функция?

2.  В чем особенность виртуальных функций? Чем они отличаются от перегружаемых?

3.  Раскройте суть понятия динамический полиморфизм.

4.  Что такое чисто виртуальные функции?

5.  Раскройте суть понятия интерфейс.

6.  Добавить виртуальную функцию для вычисления длины контура рекламного плаката (периметр, длина окружности).

Добавить свойства: стоимость единицы площади основы плаката и стоимость буквы на плакате, а также методы для вычисления стоимости основы плаката, стоимости надписи, суммарной стоимости плаката.

Создать массивы один для Rectangle -плакатов, второй для Triangle -плакатов.

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

Реализовать на основе имеющихся массивов вывод статистики работы мастерской:

для клиента – квитанцию о заказанном плакате. (Номер плаката состоит из двух частей – имя массива, в котором хранятся данные о плакате и его индекс в массиве.)

для мастера – список заказанных плакатов.

для директора – список заказанных плакатов, их стоимость, а также итоговую стоимость за день.

* Реализовать возможность делать резервную копию массивов данных в файл, а также восстановления данных из файла в соответствующие массивы.

Добавить свойства дата регистрации и выдачи плаката, а также состояние. Реализовать возможность автоматически при регистрации проставлять дату регистрации плаката, менять состояние плаката, а также автоматически проставлять дату выдачи плаката, при установке состояния выдан.

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

 

Перегрузка операций

 

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



Поделиться:


Последнее изменение этой страницы: 2019-05-20; просмотров: 415; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.239.57.87 (0.09 с.)