Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабораторная работа 18.doc
Скачиваний:
36
Добавлен:
11.04.2015
Размер:
93.7 Кб
Скачать

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

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

class P {

public:

P(void){}

virtual void hello(void) {}

};

class C: public P {

public:

C(void) {}

virtual void hello(void) { cout << “Hello world”; }

};

void main() {

P *p; C c;

p=&c;

p->hello(“Hello”); // ошибка

}

Компилятор выдаст ошибку на строке p->hello(“hello”). Компилятор может определить, что виртуальная функция hello не имеет параметров, даже если сообщение связывается с методом hello класса C во время выполнения программы. Виртуальные функции, определенные в родительском или потомственных классе имеют одинаковые списки параметров, так как позднее связывание не оказывает влияния на контроль типов параметров.

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

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

Например,

ClassName *object;

object->message(10);

преобразуется в

ClassName_message(object,10);

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

Виртуальные функции реализованы с использованием таблиц функций. Рассмотрим следующие классы.

class P {

int value;

public:

virtual int method1(float r):

virtual void method2(void);

virtual float method3(char *s);

};

class C1:public P {

public:

void method2(void);

};

class C2: public C1 {

public:

float method3(char *s);

};

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

Типичный объект, приведенных выше классов, выглядит, примерно, так:

int value;

virtual_table->P::method1

C1::method2

C2::method3

Компилятор преобразует вызов виртуальной функции в косвенный вызов через virtualTable. Например,

С2 *с;

c->method3(“Hello”);

преобразуется в

(*(c->virtual_table[2]))(c,”Hello”);

Абстрактные базовые классы

Базовый класс иерархии типа обычно содержит ряд виртуальных функций, которые обеспечивают динамическую типизацию. Часто в базовом классе эти виртуальные функции фиктивны и имеют пустое тело. Определенное значение им придают в порожденных классах. В С++ для этой цели применяется чистая виртуальная функция. Чистая виртуальная функция - виртуальная функция-член, тело которой обычно не определено. Запись этого объявления внутри класса следующая:

virtual прототип функции = 0;

Чистая виртуальная функция используется для того, чтобы “отложить” решение о реализации функции. В ООП терминологии это называется отсроченным методом.

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

Объясним эту концепцию при помощи разработки примитивной формы экологического моделирования. В нашем примере будем иметь различные формы взаимодействия жизни с использованием абстрактного базового класса living. Создадим fox (лису) как типичного хищника, и rabbit (кролика ) как его жертву. Rabbit есть grass (траву).

 

const int N = 40, STATES=4 ; // размер квадратной площади

enum state { EMPTY, GRASS, RABBIT, FOX };

 

class living;

typedef living *world[N][N]; // world будет моделью

 

class living { // что живет в мире

protected:

int row, column; // местоположение

void sums(world, int sm[]); // sm[#states] используется next

public:

living(int r,int c):row(r),column(c) {}

virtual state who()=0; // идентификация состояний

virtual living* next(world w)=0; // расчет next

};

void living::sums(world w,int sm[]) {

sm[EMPTY]=sm[GRASS]=sm[RABBIT]=sm[FOX]=0;

for(int i=-1; i <= 1; i++)

for(int j=-1; j <= 1; j++) sm[w[row+i][column+j]->who()]++;

}

 

// текущий класс - только хищники

class fox: public living {

protected:

int age; // используется для принятия решения о смерти

public:

fox(int r,int c,int a=0):living(r,c),age(a) {}

state who() { return FOX; } // отложенный метод для FOX

living* next(world w); // отложенный метод для FOX

};

// текущий класс - только жертвы

class rabbit: public living {

protected:

int age; // используется для принятия решения о смерти

public:

rabbit(int r,int c,int a=0):living(r,c),age(a) {}

state who() { return RABBIT; } // отложенный метод для RABBIT

living* next(world w); // отложенный метод для RABBIT

};

// текущий класс - только растения

class grass: public living {

public:

grass(int r,int c):living(r,c) {}

state who() { return GRASS; } // отложенный метод для GRASS

living* next(world w); // отложенный метод для GRASS

};

// жизнь отсутствует

class empty: public living {

public:

empty(int r,int c):living(r,c) {}

state who() { return EMPTY; } // отложенный метод для EMPTY

living* next(world w); // отложенный метод для EMPTY

};

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

Living* grass::next(world w) {

int sum[STATES];

sums(w,sum);

if(sum[GRASS] > sum[RABBIT) // есть траву

return (new grass(row,column));

else

return (new empty(row,column));

}

Grass может быть съеден Rabbit. Если в окрестности имеется больше grass, чем rabbit, grass остается, иначе - grass будет съедена.