русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Виртуальные функции наследуются


Дата добавления: 2015-07-09; просмотров: 666; Нарушение авторских прав


 

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

 

// D2 производный от D1, а не от В..

class D2 : public Dl {

public:

void who() { // Определение who() в классе D2

cout << "Второй производный класс\n";

}

};

 

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

 

#include <iostream>

#include <conio>

using namespace std;

 

class B {

public:

virtual void who() {

cout << "Base класс\n";

}

};

class D1 : public B {

void who() {

cout << "First derived class \n";

}

};

class D2 : public B {// D2 не переопределяет who().

// who() не определена

};

 

int main () {

 

B base_obj;

B *p;

D1 D1_obj;

D2 D2_obj;

 

p = &base_obj;

p->who(); // обращение к who() из В

 

p = &D1_obj;

p->who(); // обращение к who () из D1

 

p = &D2_obj;

p->who() ; /* обращение к who () из В, потому что D2 не переопределил who()*/

getch();

return 0;

}

 

Вот вывод этой программы:

 

Базовый класс

Первый базовый класс

Базовый класс

 

Из-за того, что класс D2 не переопределил who(), используется вариант who() , определенный в базовом классе.

Не забывайте, что наследование виртуальных функций имеет иерархический характер. Если пример изменить так, чтобы D2 был производным не от В, а от D1, тогда при вызове функции who() для объекта типа D2 будет вызываться не who() из класса В, а вариант who(), объявленный внутри D1, потому что класс D1 является ближайшим классом к D2 в иерархии классов.



 

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

 

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

 

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

#include <iostream>

#include <cstring>

#include <conio>

using namespace std;

// Класс для двумерных объектов.

class TwoDShape {

// эти члены закрыты

double width;

double height;

// добавим поле для названия фигуры

char name[20];

public :

// Конструктор по умолчанию.

TwoDShape() {

width = height = 0.0;

strcpy(name, "неизвестно");

}

// Конструктор для TwoDShape.

TwoDShape(double w, double h, char *n) {

width = w;

height = h;

strcpy(name, n);

}

// Конструируем объект с равными шириной и высотой.

TwoDShape (double x, char *n) {

width = height = x;

strcpy(name, n);

}

void showDim() {

cout << "Ширина и высота составляют "

<< width << " и " << height << "\n";

}

// функции доступа

double getWidth() { return width; }

double getHeight() { return height; }

void setWidth (double w) { width = w; }

void setHeight (double h) { height = h; }

char *getName() { return name; }

 

// Добавим в TwoDShape функцию area()и сделаем

// ее виртуальной.

virtual double area () { //Функция агеа( ) теперь виртуальная.

cout << "Ошибка: area() должна быть переопределена.\n";

return 0.0;

}

};

// Triangle является производным от TwoDShape.

class Triangle : public TwoDShape {

char style[20]; // теперь private

public:

/* Конструктор по умолчанию. Он автоматически вызывает

конструктор по умолчанию TwoDShape. */

Triangle () {

strcpy(style, "неизвестно");

}

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

Triangle(char *str, double w,

double h) : TwoDShape(w, h, "треугольник") {

strcpy(style, str);

}

// Конструируем равнобедренный треугольник.

Triangle(double x) : TwoDShape(x, "треугольник") {

strcpy(style, "равнобедренный");

}

 

// Переопределяем функцию area(), объявленную в TwoDShape.

double area () { // Переопределение area( ) в Triangle.

return getWidth() * getHeight() /2;

}

 

void showStyle () {

cout << "Треугольник" << style << "\n";

}

};

 

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

class Rectangle : public TwoDShape {

public:

// Конструируем прямоугольник.

Rectangle(double w, double h) :

TwoDShape(w, h, "прямоугольник") { }

//Конструируем квадрат.

Rectangle(double x):

TwoDShape(x, "прямоугольник") { }

bool isSquare() {

if(getWidth() == getHeight())

return true;

return false;

}

 

// Это другое переопределение функции area() .

double area () { // Переопределение area( ) в Rectangle.

return getWidth() * getHeight () ;

}

};

 

int main() {

// объявим массив указателей на объекты TwoDShape.

TwoDShape *shapes[5];

shapes[0] = &Triangle("прямоугольный", 8.0, 12.0);

shapes[1] = &Rectangle(10);

shapes [2] = &Rectangle(10, 4);

shapes [3] = &Triangle(7.0) ;

shapes [4] = &TwoDShape(10 , 20, "обобщенную фигуру") ;

 

for(int i=0; i < 5; i++) { // Теперь для каждого объекта вызываем

//правильный вариант функции агеа()

cout << "объект представляет собой " <<

shapes[i]->getName() << "\n";

cout << "Площадь равна " <<

shapes[i]->area() << "\n";

cout << "\n";

}

getch();

return 0;

}

 

Ниже приведен вывод этой программы:

 

объект представляет собой треугольник

Площадь равна 48

объект представляет собой прямоугольник

Площадь равна 100

объект представляет собой прямоугольник

Площадь равна 40

объект представляет собой треугольник

Площадь равна 24.5

объект представляет собой обобщенную фигуру

Ошибка: area() должна быть переопределена.

Площадь равна 0

 

Рассмотрим программу в деталях. Прежде всего, агеа( ) объявлена как виртуальная в классе TwoDShape и переопределена в классах Triangle и Rectangle. Внутри TwoDShape для функции агеа() предусмотрена реализация-заготовка, которая просто информирует пользователя, что функция должна быть переопределена в производном классе. Каждое переопределение агеа( ) представляет реализацию, отвечающую типу объекта, инкапсулированного в данном производном классе. Таким образом, если бы вы, например, реализовывали класс эллипсов, тогда агеа() должна была бы вычислять площадь эллипса.

В рассмотренной программе имеется одно важное место. Обратите внимание на то, что переменная shapes в main() объявлена как массив указателей на объекты TwoDShape. Однако элементам этого массива присвоены указатели на объекты Triangle, Rectangle и TwoDShape. Это допустимо, потому что указатель базового класса может указывать на объект производного класса. Программа затем просматривает этот массив в цикле, выводя информацию о каждом объекте. Будучи весьма простым, этот фрагмент тем не менее иллюстрирует мощь как наследования, так и виртуальных функций. Тип объекта, на который указывает указатель базового класса, определяется во время выполнения, и дальнейшие действия зависят от этого типа. Если объект является производным от TwoShape, его площадь можно получить, вызвав функцию агеа(), причем интерфейс этой операции одинаков, для какого бы типа фигуры он не использовался.

 



<== предыдущая лекция | следующая лекция ==>
Основы виртуальных функций | Чистые виртуальные функции и абстрактные классы


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.199 сек.