Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lucik_op.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
2.88 Mб
Скачать

Абстрактные классы

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

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

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

base a;

base *p= new base;

компилятор выдаст сообщение об ошибке. В то же время вполне можно использовать инструкции вида

rect b;

base *p=&b;

base &p=b;

Чисто виртуальную функцию, как и просто виртуальную функцию, не обязательно переопределять в производных классах. При этом если в производном классе она не переопределена, то этот класс тоже будет абстрактным, и при попытке создать объект этого класса компилятор выдаст ошибку. Таким образом, забыть переопределить чисто виртуальную функцию невозможно. Абстрактный базовый класс навязывает определенный интерфейс всем производным от него классам. Главное назначение абстрактных классов – в определении интерфейса для некоторой иерархии классов.

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

class base

{ компоненты-данные

public:

virtual ~base() = 0;

компоненты-функции

}

base ::~base()

{реализация деструктора}

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

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

#include "iostream.h"

#include "conio.h"

// моделирование хищник - жертва с использованием

// иерархии классов

const int N=6, // размер квадратной площади (мира)

STATES=4, // количество видов жизни

DRAB=5,DFOX=5, // количество циклов жизни кролика и лиса

CYCLES=10; // общее число циклов моделирования мира

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

class living; // forvard объявление

typedef living *world[N][N]; // world- модель мира

void init(world);

void gener(world);

void update(world,world);

void dele(world);

class living

{protected:

int row,col; // местоположение в модели

void sums(world w,int sm[]); //

public:

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

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

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

virtual void print()=0; // вывод содержимого поля модели

};

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

{ int i,j;

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

int i1=-1,i2=1,j1=-1,j2=1;

if(row==0) i1=0; // координаты внешних клеток модели

if(row==N-1) i2=0;

if(col==0) j1=0;

if(col==N-1) j2=0;

for(i=i1;i<=i2;++i)

for(j=j1;j<=j2;++j)

sm[w[row+i][col+j]->who()]++;

}

В базовом классе living объявлены две чисто виртуальные функции - who() и next() и одна обычная функция sums(). Моделирование имеет правила для решения о том, кто продолжает жить в следующем цикле. Они основаны на соседствующих популяциях в некотором квадрате. Глубина иерархии наследования – один уровень.

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

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;} // отложенный метод для foxes

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

void print(){cout << " ли ";}

};

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

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

void print(){cout << " кр ";}

};

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

class grass:public living

{ public:

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

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

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

void print(){cout << " тр ";}

};

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

class empty : public living

{ public:

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

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

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

void print(){cout << " ";}

};

Характеристика поведения каждой формы жизни фиксируется в версии next(). Если в окрестности имеется больше grass, чем rabbit, grass остается, иначе grass будет съедена.

living *grass::next(world w)

{ int sum[STATES];

sums(w,sum);

if(sum[GRASS]>sum[RABBIT]) // кролик ест траву

return (new grass(row,col));

else

return(new empty(row,col));

}

Если возраст rabbit превышает определенное значение DRAB, он умирает либо, если поблизости много лис, он может быть съеден.

living *rabbit::next(world w)

{ int sum[STATES];

sums(w,sum);

if(sum[FOX]>=sum[RABBIT]) // лис ест кролика

return (new empty(row,col));

else if(age>DRAB) // кролик слишком старый

return(new empty(row,col));

else

return(new rabbit(row,col,age+1)); // кролик постарел

}

Fox тоже умирает от старости.

living *fox::next(world w)

{ int sum[STATES];

sums(w,sum);

if(sum[FOX]>5) // слишком много лис

return (new empty(row,col));

else if(age>DFOX) // лис слишком старый

return(new empty(row,col));

else

return(new fox(row,col,age+1)); // лис постарел

}

// заполнение пустой площади

living *empty::next(world w)

{ int sum[STATES];

sums(w,sum);

if(sum[FOX]>1) // первыми добавляются лисы

return (new fox(row,col));

else if(sum[RABBIT]>1) // вторыми добавляются кролики

return (new rabbit(row,col));

else if(sum[GRASS]) // третьими добавляются растения

return (new grass(row,col));

else return (new empty(row,col));// иначе пусто

}

Массив world представляет собой контейнер для жизненных форм. Он должен иметь в собственности объекты living, чтобы распределять новые и удалять старые.

// world полностью пуст

void init(world w)

{ int i,j;

for(i=0;i<N;++i)

for(j=0;j<N;++j)

w[i][j]=new empty(i,j);

}

// генерация исходной модели мира

void gener(world w)

{ int i,j;

for(i=0;i<N;++i)

for(j=0;j<N;++j)

{ if(i%2==0 && j%3==0) w[i][j]=new fox(i,j);

else if(i%3==0 && j%2==0) w[i][j]=new rabbit(i,j);

else if(i%5==0) w[i][j]=new grass(i,j);

else w[i][j]=new empty(i,j);

}

}

// вывод содержимого модели мира на экран

void pr_state(world w)

{ int i,j;

for(i=0;i<N;++i)

{ cout<<endl;

for(j=0;j<N;++j)

w[i][j]->print();

}

cout << endl;

}

// новый world w_new рассчитывается из старого world w_old

void update(world w_new, world w_old)

{ int i,j;

for(i=0;i<N;++i)

for(j=0;j<N;++j)

w_new[i][j]=w_old[i][j]->next(w_old);

}

// очистка мира

void dele(world w)

{ int i,j;

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

for(j=1;j<N-1;++j) delete(w[i][j]);

}

Модель имеет odd и even мир. Их смена является основой для расчета последующего цикла.

int main()

{ world odd,even;

int i;

init(odd);

init(even);

gener(even); // генерация начального мира

cout<<"1 цикл жизни модели"<<endl;

pr_state(even); // вывод сгенерированной модели

for(i=0;i<CYCLES-1;++i) //цикл моделирования

{ getch();

cout<<i+2<<" цикл жизни модели"<<endl;

if(i%2)

{ update(even,odd); // создание even модели из odd модели

pr_state(even); // вывод сгенерированной модели even

dele(odd); // удаление модели odd

}

else

{ update(odd,even); // создание odd модели из even модели

pr_state(odd); // вывод сгенерированной модели odd

dele(even); // удаление модели even

}

}

return 1;

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]