- •Наследование.
- •Производный класс
- •В файле student2.H
- •В файле student2.H
- •В файле student2.H
- •В файле student2.Cpp
- •Повторное использование кода: класс двоичного дерева
- •В файле gentree2.H
- •В файле gentree2.Cpp
- •Виртуальные функции
- •В файле virt_sel.Cpp
- •В файле virt_err.Cpp
- •В файле shape2.Cpp
- •Virtual double area () const {return 0;} //площадь
- •Абстрактные базовые классы
- •В файле predator.Cpp
- •Шаблоны и наследование
- •Порядок выполнения конструкторов
- •Наследование и проектирование
- •Форма подтипов
- •Идентификация типа на этапе выполнения
- •В файле typied.Cpp
В файле predator.Cpp
//Модель хищник–добыча с использованием класса living
const int N = 40; // размер квадратного поля
//состояние клетки: пусто, трава, кролик, трава, кролик, лиса
//STATES – количество различных состояний
enum state { EMPERTY, GRASS, RABBIT, FOX, STATES };
const int DRAB = 3, DFOX = 6; CYCLES = 5;
class living; //предварительное объявление
typedef living* world[N] [N]; //мир
class living { //что живет на свете
public:
virtual state who() = 0; //выяснение состояния (кто?)
virtual living* next(world w) = 0; //что дальше?
protected:
int row, column; //координаты поля
void sums(world w, int sm[];
};
void living::sums(world w, int sm[];
{
int i, j;
sm[EMPERTY] = sm[GRASS] = sm[RABBIT]= sm[FOX] = 0;
for ( i =-1; i<= 1; ++i)
for ( j =-1; j<= 1; ++j)
sm[w[row + i][column +j] -> who() ++;
}
Здесь две чисто виртуальных функции и одна обычная функция-член – sums(). Виртуальные функции влекут небольшие дополнительные издержки на этапе выполнения по сравнению с обычными функциями-членами. Поэтому мы используем их в наших реализациях, только когда это необходимо. В модели предусмотрены правила для определения того, кто продолжит существование в следующем цикле, в зависимости от популяций по соседству с заданным квадратом. Эти популяции вычисляются с помощью sums().Все напоминает «Жизнь» Конвея.
Иерархия наследования будет одноуровневой:
//здесь – хищник(лиса)
class fox : public living {
public:
fox(int r, int c, int a = 0) : age(a)
{ row = r; column = c; }
state whj() { return FOX; } //отложенный метод для лис
living* next(world w);
protected:
int age; //возраст
};
//здесь – добыча (кролик)
class rabbit : public living {
public:
rabbit(int r, int c, int a = 0) : age(a)
{ row = r; column = c; }
state who() { return RABBIT; }
living* next(world w);
protected:
int age;
};
//здесь – растительная жизнь (трава)
class grass : public living {
public:
grass(int r, int c) { row = r; column = c; }
syate who() {return GRASS;}
living* next(world w);
};
//здесь нет жизни (пусто)
class empty : public living {
public:
empty (int r, int c) { row = r; column = c; }
syate who() {return EMPTY;}
living* next(world w);
};
Обратите внимание, что данная схема позволяет с помощью последующих уровней наследования разрабатывать другие формы хищников, добычи и растительной жизни. Характеристики того, как каждая форма жизни будет себя вести, сосредоточены в принадлежащей ей 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));
}
Кролики умирают от старости, если их возраст превышает некий определенный предел DRAB, или их съедают, если по соседству имеется достаточное количество лис.
living* rabbit::next(world w);
{
int sum[STATES];
sums(w,sum);
if (sum[FOX] >= sum[RABBIT] ) //едим кроликов
return (new empty(row, column));
else if (age > DRAB)
return (new empty(row, column));
else
return (new rabbit(row, column, age + 1));
}
Лисы же умирают от перенаселения или от старости:
living* fox::next(world w);
{
int sum[STATES];
sums (w, sum);
if (sum [FOX] >5) // ну и лис развелось!
return (new empty (row, column));
else if (age > DFOX) // лиса состарилась
return (new empty (row, column));
else
return (new fox( row, column, age = 1));
}
За пустые квадраты конкурируют разные формы жизни:
//как заполнить пустой квадрат
living* empty::next( word w0
[
int sum [STATES];
sums(w, sum);
if (sum[FOX] >1)
return (new fox(row, column));
else if (sum[RABBIT] >1)
return (new rabbit(row, column));
else if (sum[GRASS])
return (new grass(row, column));
else
return (new empty(row, column));
}
Правила в различных версиях next() задают сложный вариант (в разумных пределах) набор взаимодействий. Конечно, чтобы сделать мир более интересным, можно смоделировать другие варианты поведения, такие как половое размножение, когда животные имеют пол и могут спариваться.
Тип массива world является контейнером для жизни. Контейнер отвечает за свое текущее состояние. Он должен иметь «право собственности» на объекты living, чтобы размещать новые и уничтожать старые:
// начало: мир пуст
void invit (word w)
{
int I, j;
for (I = 0; I<N; ++i)
for (j = 0; j<N; ++i)
w[I][j] = new empty (i,j);
}
//новый мир w_new вычисляется из старого w_old
void update(world w_new, world_old)
{
int i, j;
for (i = 1; i<N – 1; ++i) //за границы нельзя
for (j = 1; j<N – 1; ++j)
w-new [i][j] -> next(w_old);
}
// очистка мира
void dele(word w0
[
int i, j;
for (i=1; I<N – 1; ++i)
for (j = 1; j<N – 1; ++j)
delete(w([i][j]);
}
Модель имеет миры even и odd (четный и нечетный), которые чередуются в качестве базиса для вычислений следующего цикла:
Int main()
{
world odd, even;
int i;
init(odd); init(even);
even(even): //генерирует начальный мир – рай
pr_state(even); //выводит состояние райского сада
for (i = 0; i<CYCLES; ++I) { //моделирование
if (I % 2) {
update(even, odd);
pr_state9even);
dele(odd);
}
else {
update9odd, even);
pr_state(odd);
dele9even);
}
}
}