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

Приложение а

(рекомендуемое)

Листинг модуля Algo.h

#ifndef AlgoH

#define AlgoH

//---------------------------------------------------------------------------

// Заголовочный файл "iostream.h" содержит описание потоков ввода (класс istream)

// и вывода (класс ostream).

// Вывод (в поток вывода) осуществляется перегруженной операцией <<.

// Ввод (из потока ввода) осуществляется перегруженной операцией >>.

// Также объявляются стандартные поток ввода cin типа istream и

// поток вывода cout типа ostream.

#include <iostream.h>

//---------------------------------------------------------------------------

/*

Шаблон функции: нахождение максимума из двух значений одного типа.

Аргументы:

константная ссылка x на первое значение,

константная ссылка y на второе значение.

Результат: константная ссылка на максимальное значение.

Требование к типу T:

- должна быть определена операция <

bool operator < (const T &x, const T &y) { ... }

*/

template <class T>

inline const T& Max(const T &x, const T &y)

{

return x < y ? y : x;

}

//---------------------------------------------------------------------------

/*

Шаблон функции: нахождение минимума из двух значений одного типа.

Аргументы:

константная ссылка x на первое значение,

константная ссылка y на второе значение.

Результат: константная ссылка на минимальное значение.

Требование к типу T: аналогично Max

*/

template <class T>

inline const T& Min(const T &x, const T &y)

{

return x < y ? x : y;

}

//---------------------------------------------------------------------------

/*

Шаблон функции: копирование массивов равного размера из элементов одинакового типа.

Аргументы:

указатель x на первый элемент результирующего массива,

указатель y на константный объект - адрес первого элемента исходного массива,

count - число элементов в массивах.

Результат: -

Требование к типу T:

- операция присваивания должна быть корректно определена

*/

template <class T>

void Copy(T *x, const T *y, unsigned count)

{

while (count--)

*x++ = *y++;

}

//---------------------------------------------------------------------------

/*

Шаблон функции: сравнение массивов равного размера из элементов одинакового типа.

Аргументы:

указатель x на константный объект - адрес первого элемента одного массива,

указатель y на константный объект - адрес первого элемента второго массива,

count - число элементов в массивах.

Результат: true, если массивы равны; false - иначе.

Требование к типу T:

- должна быть определена операция !=

bool operator != (const T &x, const T &y) { ... }

*/

template <class T>

bool Compare(const T *x, const T *y, unsigned count)

{

while (count--)

if (*x++ != *y++) return false;

return true;

}

//---------------------------------------------------------------------------

/*

Шаблон функции: ввод массива из потока.

Аргументы:

указатель x на первый элемент массива,

count - число элементов в массиве,

ссылка на поток ввода stream (по умолчанию стандартный поток ввода cin).

Результат: -

Требование к типу T:

- должна быть определена операция ввода из потока >>

istream& operator >> (istream &stream, T &x) { ... }

*/

template <class T>

void Read(T *x, unsigned count, istream &stream = cin)

{

while (count--)

stream >> *x++;

}

//---------------------------------------------------------------------------

/*

Шаблон функции: вывод массива в поток.

Аргументы:

указатель x на константый объект - адрес первого элемента массива,

count - число элементов в массиве,

ссылка поток вывода stream (по умолчанию стандартный поток вывода cout),

указатель delim на константую строку,

которая выводится после каждого элемента массива (по умолчанию " ").

Результат: -

Требование к типу T:

- должна быть определена операция вывода в поток <<

ostream& operator << (ostream &stream, const T &x) { ... }

*/

static const char *WriteDefDelim = " ";

template <class T>

void Write(const T *x, unsigned count, ostream &stream = cout, const char *delim = WriteDefDelim)

{

while (count--)

stream << *x++ << delim;

}

//---------------------------------------------------------------------------

#endif

Приложение Б

(рекомендуемое)

Листинг модуля Array.h

#ifndef ArrayH

#define ArrayH

//---------------------------------------------------------------------------

#include <assert.h>

#include "Algo.h"

//---------------------------------------------------------------------------

/*

Шаблон класса: одномерный динамический массив из элементов типа T.

*/

template <class T>

class Array {

private:

unsigned FCount; // число элементов

T* FData; // указатель на первый элемент массива (если FCount > 0)

public:

// Создание массива из count элементов, по умолчанию 0.

Array(unsigned count = 0)

: FCount(0) // инициализируем элемент FCount конструктором копии

{ // (вместо конструктора по умолчанию)

Resize(count, false);

}

// Создание массива из count элементов, которые инициализируются

// count элементами, на которые указывает data.

Array(unsigned count, const T *data)

: FCount(0)

{

Assign(count, data);

}

// Конструктор копии.

Array(const Array& array)

: FCount(0)

{

Assign(array.FCount, array.FData);

}

// Деструктор (освобождает память).

~Array()

{

Clear();

}

// Возвращает размер массива.

unsigned Count() const { return FCount; }

// Задает размер массива.

void Count(unsigned count) { Resize(count); }

// Устанавливает размер массива в count и копирует в него count элементов

// data[0], data[1], ..., data[count - 1].

void Assign(unsigned count, const T *data);

// Устанавливает размер массива. Старые элементы (сколько влезут в новый размер)

// по умолчанию остаются (параметр store = true), либо теряются (store = false).

void Resize(unsigned count, bool store = true);

// Удаление всех элементов.

void Clear()

{

Count(0);

}

// Операция присваивания. Устанавливается такой же размер и копируются данные из array.

Array& operator =(const Array& array)

{

// Если не имеет место самоприсваивание (Array<T> a; a = a;), то

if (this != &array)

// выполняем присваивание.

Assign(array.FCount, array.FData);

// Возвращаем ссылку на левый аргумент операции присваивания, чтобы позволить, например,

// дальнейшее присваивание (Array<T> a, b, c; a = b = c;).

return *this;

}

// Операция индексации (для константного массива).

const T& operator [](unsigned index) const

{

assert(index < FCount); // проверка корректности индекса

return FData[index]; // и возврат константной ссылки на элемент

}

// Операция индексации (для неконстантного массива).

T& operator [](unsigned index)

{

assert(index < FCount); // проверка корректности индекса

return FData[index]; // и возврат ссылки на элемент

}

// Операция вывода в поток.

friend ostream& operator <<(ostream &stream, const Array& array)

{

// Вывод в поток и

Write(array.FData, array.FCount, stream);

// возврат ссылки на поток, чтобы позволить последующий вывод (нап: cout << a << b).

return stream;

}

// Операция ввода из потока.

friend istream& operator >>(istream &stream, Array& array)

{

// Ввод из потока и

Read(array.FData, array.FCount, stream);

// возврат ссылки на поток, чтобы позволить последующий ввод (нап: cin >> a >> b).

return stream;

}

// Операция равенства.

friend bool operator ==(const Array& x, const Array& y)

{

// Если массивы являются различными объектами, то выполняем сравнение.

if (&x != &y)

// Если число элементов одинаково,

if (x.FCount == y.FCount)

// то выполняем поэлементное сравнение.

return Compare(x.FData, y.FData, FCount);

// Иначе, массивы не равны.

else

return false;

// Иначе возвращаем истину (т. к. любой массив сам себе равен).

return true;

}

// Операция неравенства.

friend bool operator !=(const Array& x, const Array& y)

{

return !(x == y);

}

};

//---------------------------------------------------------------------------

// Определение не встраиваемых функций-элементов

//---------------------------------------------------------------------------

template <class T>

void Array<T>::Assign(unsigned count, const T *data)

{

Resize(count, false); // устанавливаем размер, без сохранения элементов

Copy(FData, data, FCount); // и копируем данные

}

//---------------------------------------------------------------------------

template <class T>

void Array<T>::Resize(unsigned count, bool store)

{

// Если число элементов изменяется.

if (FCount != count) {

// Если новое число элементов не нулевое, то распределяем память;

if (count) {

// Создаем динамический массив из count элементов,

T *p = new T[count];

// и копируем туда старые элементы (сколько влезет), если требуется.

if (store)

Copy(p, FData, Min(FCount, count));

// Уничтожаем старый массив, если он не пуст.

if (FCount) delete[] FData;

// Сохраняем в классе адрес первого элемента нового массива.

FData = p;

}

// иначе освобождаем память.

else

delete[] FData;

// Сохраняем новое число элементов в классе.

FCount = count;

}

}

//---------------------------------------------------------------------------

#endif

Приложение В

(рекомендуемое)

Листинг модуля List.h

//---------------------------------------------------------------------------

#ifndef ListH

#define ListH

// Определяем макрос, кот-й используется как условие того,

// что компилируется "List.h".

#define ListInterior

//---------------------------------------------------------------------------

#include <assert.h>

//---------------------------------------------------------------------------

/*

Шаблон класса: связный список из элементов типа T.

*/

template <class T>

class List {

private:

// Включаем определение структуры "узел списка".

#include "ListNode.h"

Node *FFirst, // первый узел списка

*FLast; // последний узел

// Делаем конструктор копии и операцию присваивания закрытыми.

List(const List&) { }

void operator =(const List&) { }

public:

// Включаем определение класса "итератор списка".

#include "ListIterator.h"

// Конструктор пустого списка

List()

: FFirst(NULL), FLast(NULL)

{

}

// Деструктор (освобождает память).

~List()

{

Clear();

}

// Возвращает константный итератор на первый элемент (для константного списка).

const Iterator First() const

{

return Iterator(FFirst);

}

// Возвращает итератор на первый элемент (для не константного списка).

Iterator First()

{

return Iterator(FFirst);

}

// Возвращает константный итератор на последний элемент (для константного списка).

const Iterator Last() const

{

return Iterator(FLast);

}

// Возвращает итератор на последний элемент (для не константного списка).

Iterator Last()

{

return Iterator(FLast);

}

// Возвращает константный итератор на "конец" (для константного списка).

const Iterator End() const

{

return Iterator();

}

// Возвращает итератор на "конец" (для не константного списка).

Iterator End()

{

return Iterator();

}

// Проверка пустоты списка.

bool Empty() const

{

return !FFirst;

}

// Вставляет элемент в список перед элементом, на который указывает итератор iter.

// Если iter указывает на "конец", то эл-т добавляется в конец списка.

void Insert(const Iterator& iter, const T& data);

// Удаляет эл-т, на который указывает итератор iter.

void Delete(const Iterator& iter);

// Удаляет все эл-ты из списка.

void Clear();

// Добавление эл-та в начало списка.

void PushFront(const T& data) { Insert(First(), data); }

// Добавление эл-та в конец списка.

void PushBack(const T& data) { Insert(End(), data); }

// Удаление первого эл-та.

void PopFront() { Delete(First()); }

// Удаление последнего эл-та.

void PopBack() { Delete(Last()); }

};

//---------------------------------------------------------------------------

// Определение не встраиваемых функций-элементов

//---------------------------------------------------------------------------

template <class T>

void List<T>::Insert(const Iterator& iter, const T& data)

{

Iterator i;

Node *t,

*n = new Node(data); // создаем отдельный узел

// Если iter указывает на "конец", то добавляем эл-т в конец списка.

if (iter == End()) {

// Если список не пуст, то

if (FFirst)

// связываем последний узел с новым;

FLast->Next = n;

else

// иначе новый последний узел является и первым.

FFirst = n;

// Связываем новый узел с последним,

n->Prev = FLast;

// и делаем новый последним.

FLast = n;

}

else

// Иначе, если iter указывает на первый, то добавляем эл-т в начало списка.

if (iter == First()) {

// Если список не пуст, то

if (FFirst)

// связываем первый узел с новым;

FFirst->Prev = n;

else

// иначе новый первый узел является и последним.

FLast = n;

// Связываем новый узел с первым,

n->Next = FFirst;

// и делаем его первым.

FFirst = n;

}

// Иначе вставляем эл-т в "середину" списка.

else {

// Ищем узел X, перед которым нужно вставить.

for (i = First(); i != End(); i++)

if (i == iter) break;

// Если не найден, то iter не корректен: ошибка.

assert(i != End());

t = i.P->Prev;

t->Next = n; // связываем предшествующий X с новым

n->Prev = t; // связываем новый с предшествующим X

n->Next = i.P; // связываем новый с X

i.P->Prev = n; // связываем X с новым

}

}

//---------------------------------------------------------------------------

template <class T>

void List<T>::Delete(const Iterator& iter)

{

Node *t;

Iterator i;

// Ищем узел X, содержащий удаляемый эл-т.

for (i = First(); i != End(); i++)

if (i == iter) break;

// Если не найден, то iter не корректен: ошибка.

assert(i != End());

t = i.P;

// Если X первый.

if (i == First()) {

// Смещаем первый узел на следующий за ним.

FFirst = t->Next;

// Если он отсутсвует, то список состоит из одного эл-та и

// нужно сбросить указатель на последний.

if (!FFirst) FLast = NULL;

}

else

// Иначе, если X последний.

if (i == Last()) {

// Смещаем последний узел на ему предшествующий.

FLast = t->Prev;

// Если он отсутсвует, то список состоит из одного эл-та и

// нужно сбросить указатель на первый.

if (!FLast) FFirst = NULL;

}

else {

// Иначе X средний.

t->Prev->Next = t->Next; // связываем предыдущий X со следующим за X,

t->Next->Prev = t->Prev; // отделяя X от списка

}

// Удаляем X (уничтожаем и освобождаем память).

delete t;

}

//---------------------------------------------------------------------------

template <class T>

void List<T>::Clear()

{

// Если список не пуст.

if (!Empty()) {

Node *n;

Iterator i;

// Удаляем все узлы.

for (i = First(); i != End(); ) {

n = i.P; // сохраняем адрес узла

i++; // переходим к следующему

delete n; // и удаляем текущий

}

// Сбрасываем указатели на первый и последний.

FFirst = FLast = NULL;

}

}

//---------------------------------------------------------------------------

// Убираем макрос ListInterior, т. к. "List.h" закончился.

#undef ListInterior

#endif

Листинг модуля ListIterator.h

// Если "ListIterator.h" включен вне "List.h",

#ifndef ListInterior

// то выдать ошибку компиляции.

#error "ListIterator.h" is internal for "List.h". Do not include directly.

#endif

//template <class T>

//class List {

//...

// Итератор списка.

class Iterator {

// Класс список объявляется дружественным для доступа к указателю на узел.

friend List<T>;

Node* P; // ук-ль на узел списка, содержащий значение эл-та, на кот-й указывает итератор

public:

// Создает итератор, указывающий на эл-т списка, хранящийся в узле по адресу p.

// По умолчанию ни на что не указывает. Т. к. Node - закрытый тип класса List,

// то итератор на эл-т может быть создан только функциями-элементами List.

Iterator(Node* p = NULL)

: P(p)

{

}

// Операция разыменовывания для константного итератора.

// Возвращает константую ссылку на значение эл-та.

const T& operator *() const

{

return P->Data;

}

// Операция разыменовывания для не константного итератора.

// Возвращает не константного ссылку на значение эл-та, по которой

// оно может быть изменено.

T& operator *()

{

return P->Data;

}

// Префиксная операция инкремента. Перемещает итератор к следующему эл-ту списка.

Iterator operator ++()

{

P = P->Next; // переходим к следующему эл-ту

return *this; // возвращаем "увеличенный" итератор

}

// Постфиксная операция инкремента. Перемещает итератор к следующему эл-ту списка.

// Аргумент типа int указывает компилятору, что данная функция-операция operator ++

// является постфиксной.

Iterator operator ++(int)

{

Iterator t(*this); // сохраняем "не увеличенное" значение итератора

P = P->Next; // переходим к следующему эл-ту

return t; // возвращаем "не увеличенное" значение итератора

}

// Префиксная операция декремента. Перемещает итератор к предыдущему эл-ту списка.

Iterator operator --()

{

P = P->Prev; // переходим к предыдущему эл-ту

return *this; // возвращаем "уменьшенный" итератор

}

// Постфиксная операция декремента. Перемещает итератор к предыдущему эл-ту списка.

// Аргумент типа int указывает компилятору, что данная функция-операция operator --

// является постфиксной.

Iterator operator --(int)

{

Iterator t(*this); // сохраняем "не уменьшенное" значение итератора

P = P->Prev; // переходим к предыдущему эл-ту

return t; // возвращаем "не уменьшенное" значение итератора

}

// Операция равенства.

friend bool operator ==(const Iterator& x, const Iterator& y)

{

return x.P == y.P;

}

// Операция неравенства.

friend bool operator !=(const Iterator& x, const Iterator& y)

{

return x.P != y.P;

}

};

Листинг модуля ListNode.h

// Если "ListNode.h" включен вне "List.h",

#ifndef ListInterior

// то выдать ошибку компиляции.

#error "ListNode.h" is internal for "List.h". Do not include directly.

#endif

//template <class T>

//class List {

//...

// Узел списка.

struct Node {

T Data; // данные (значение эл-та списка)

Node *Prev, *Next; // указатель на предыдущий и следующий узлы

// Создание отдельного узла.

Node(const T& data)

: Data(data), // копируем данные;

Prev(NULL), // предыдущего и

Next(NULL) // последующего узлов нет

{

} };

Приложение Г

(рекомендуемое)

Листинг модуля Classes.h

//---------------------------------------------------------------------------

#ifndef ClassesH

#define ClassesH

#include <graphics.hpp>

#include "Array.h"

#include "List.h"

//---------------------------------------------------------------------------

// Абстрактный класс "Фигура".

class CFigure {

private:

TColor FColor; // цвет фигуры (используется при цветном изображении)

public:

// Создание фигуры заданного цвета (по умолчанию белый).

CFigure(TColor c = clWhite)

: FColor(c)

{

}

// Деструктор. ОБЯЗАТЕЛЬНО должен быть виртуальным у полиморфных (использующих

// виртуальные функции) классов. См. Unit1.cpp, TForm1::FreeFigures.

virtual ~CFigure()

{

}

// Возвращает цвет фигуры.

TColor Color() const

{

return FColor;

}

// Устанавливает цвет фигуры.

void Color(TColor c)

{

FColor = c;

}

// Изображает фигуру: определяют производные классы, реализующие конкретные фигуры.

// Считается, что фигура отображается текущим цветом пера.

virtual void Draw(TCanvas *canvas) const = 0;

// Изображает фигуру в цвете: устанавливает цвет пера в FColor и вызывает Draw.

virtual void ColorDraw(TCanvas *canvas) const;

};

//---------------------------------------------------------------------------

// Конкретный класс: Точка.

class CPoint: public CFigure {

// Параметры точки являются защищенными элементами, чтобы

// потомки могли обратиться к ним напрямую (для упрощения реализации).

protected:

int FX, FY; // координаты точки

public:

// Создает точку в начале координат.

CPoint()

: FX(0), FY(0)

{

}

// Создает точку с заданными координатами.

CPoint(int x, int y)

: FX(x), FY(y)

{

}

// Считывание/задание координат точки.

int X() const { return FX; }

void X(int x) { FX = x; }

int Y() const { return FY; }

void Y(int y) { FY = y; }

// Изображение точки.

virtual void Draw(TCanvas *canvas) const;

// Изображение точки в цвете: наследуется реализация CFigure.

};

//---------------------------------------------------------------------------

// Конкретный класс: Окружность. От точки наследует координаты центра.

class CCircle: public CPoint {

protected:

int FR; // радиус окружности

public:

// Окружность с центром в начале координат нулевого радиуса.

CCircle()

: FR(0)

{

}

// Окружность с заданными координатами центра и радиусом.

CCircle(int x, int y, int r)

: CPoint(x, y), FR(r)

{

}

// Окружность с центром в заданной точке и указанным радиусом.

CCircle(const CPoint &p, int r)

: CPoint(p), FR(r)

{

}

// Считывание/задание радиуса окружности.

int R() const { return FR; }

void R(int r) { FR = r; }

// Изображение окружности.

virtual void Draw(TCanvas *canvas) const;

// Изображение окружности в цвете: наследуется реализация CFigure.

};

//---------------------------------------------------------------------------

// Конкретный класс: Эллипс. От окружности наследует координаты центра и горизонтальную полуось.

class CEllipse: public CCircle {

protected:

int FVR; // вертикальная полуось

public:

// Эллипс с центром в начале координат с нулевыми полуосями.

CEllipse()

: FVR(0)

{

}

// Эллипс с заданными координатами центра и полуосями.

CEllipse(int x, int y, int r, int vr)

: CCircle(x, y, r), FVR(vr)

{

}

// Эллипс с центром в заданной точке и указанными полуосями.

CEllipse(CPoint &p, int r, int vr)

: CCircle(p, r), FVR(vr)

{

}

// Вытягивание указанной окружности до эллипса с заданной вертикальной полуосью.

CEllipse(CCircle &c, int vr)

: CCircle(c), FVR(vr)

{

}

// Считывание/задание вертикальной полуоси.

int VR() const { return FVR; }

void VR(int vr) { FVR = vr; }

// Изображение эллипса.

virtual void Draw(TCanvas *canvas) const;

// Изображение эллипса в цвете: наследуется реализация CFigure.

};

//---------------------------------------------------------------------------

// Задание синонима типа для конкретного класса: массива точек, определенного по шаблону.

typedef Array<CPoint> PointArray;

// Конкретный класс: Ломаная. Как фигура происходит от CFigure.

// От PointArray наследует возможность задания числа точек-узлов, обращение

// к ним через операцию индексами и проч.

class CPolyline : public CFigure, public PointArray {

public:

// Пустая ломаная (из нуля узлов).

CPolyline()

{

}

// Ломаная из заданного числа узлов с координатами узлов, которые копируются,

// начиная с указанного адреса.

CPolyline(unsigned count, CPoint *coords)

: PointArray(count, coords)

{

}

// Ломаная из заданных в массиве точек узлов.

CPolyline(const PointArray &coords)

: PointArray(coords)

{

}

// Изображение ломаной.

virtual void Draw(TCanvas *canvas) const;

// Изображение ломаной в цвете: наследуется реализация CFigure.

};

//---------------------------------------------------------------------------

// Задание синонима типа для конкретного класса: связного списка указателей

// на CFigure, определенного по шаблону.

typedef List<CFigure*> FigureList;

// Конкретный класс: Множество фигур. Как фигура происходит от CFigure.

// От FigureList наследует возможность управления связным списком

// указателей на CFigure.

// Особенности реализации:

// 1) Класс хранит не сами Фигуры, содержащиеся в множестве, а лишь указатели

// на них. Т. е. использующий класс должен сам создавать эти фигуры

// и следить за их корректным уничтожением.

// 2) Т. к. множество не может содержать одинаковых элементов (а список может),

// то FigureList является закрытым базовым классом, чтобы исключить возможность

// использование тех функций списка, которые могут нарушить единственность элементов.

// 3) Класс не дает добавить себя как элемент, что приведет к бесконечной рекурсии

// при вызове функции Draw. Однако, более сложные случаи не отслеживаются, т. е.

// ответственность переносится на использующего класс. Например, множество фигур A

// содержит множество фигур B, а B содержит A.

class CFigureSet : public CFigure, private FigureList {

public:

// Делаем открытыми те элементы FigureList, которые считаем пригодыми для использования

// без изменений.

FigureList::Iterator; // вложенный класс Iterator

FigureList::First; // итератор на первый,

FigureList::Last; // последний и

FigureList::End; // "конечный" элементы

FigureList::Empty; // проверка пустоты

FigureList::Delete; // удаление элемента

FigureList::Clear; // удаление всех элементов

// Пустое множество фигур.

CFigureSet()

{

}

// Изображение множества фигур.

virtual void Draw(TCanvas *canvas) const;

// Изображение множества фигур: реализация в CFigure не подходит.

virtual void ColorDraw(TCanvas *canvas) const;

// Добавление фигуры в множество.

void Add(CFigure &f);

// Операция запятая (левый аргумент - множество фигур, правый - фигура):

// добавление фигур в множество.

CFigureSet& operator ,(CFigure &f)

{

Add(f); // добавляем фигуру

return *this; // возвращаем ссылку на себя, для возможности дальнейшего добавления

}

// Пример. Пусть A - множество фигур; a, b, c, d - фигуры.

// Вычисление выражения A, a, b, c приведет к добалению a, b, c в A.

// Сначала вычисляется (A, a) и a добавляется в A; результат - ссылка на A,

// т. е. дальше вычисляется (A, b) и т. д.

// Операция присваивания множеству фигур фигуры:

// Удаляет все фигуры из множества, добавляет указанную и возвращает ссылку на себя.

// Дальше можно использовать операцию запятая.

CFigureSet& operator =(CFigure &f)

{

Clear(); // удаляем все фигуры

Add(f); // добавляем указанную фигуру

return *this; // возвращаем ссылку на себя, для возможности применения операции запятая

}

// Пример.

// Вычисление выражения A = b, c, d приведет к созданию множества из фигур b, c, d.

// Операция сложения с присваиванием: добавление фигуры в множество.

// Введена для того, чтобы сделать текст программы более понятным.

CFigureSet& operator +=(CFigure &f)

{

Add(f); // добавляем фигуру

return *this; // возвращаем ссылку на себя, для возможности применения операции запятая

}

// Пример.

// Теперь для добавления в множество A фигур a, b, c можно писать A += a, b, c

// вместо A, a, b, c.

};

//---------------------------------------------------------------------------

#endif

Листинг модуля Classes.cpp

//---------------------------------------------------------------------------

#pragma hdrstop

#include "Classes.h"

//---------------------------------------------------------------------------

// CFigure

//---------------------------------------------------------------------------

void CFigure::ColorDraw(TCanvas *canvas) const

{

canvas->Pen->Color = FColor; // задаем цвет пера

Draw(canvas); // изображаем фигуру

}

//---------------------------------------------------------------------------

// CPoint

//---------------------------------------------------------------------------

void CPoint::Draw(TCanvas *canvas) const

{

// Ставим точку цвета пера.

canvas->Pixels[FX][FY] = canvas->Pen->Color;

}

//---------------------------------------------------------------------------

// CCircle

//---------------------------------------------------------------------------

void CCircle::Draw(TCanvas *canvas) const

{

// Делаем кисть пустой и рисуем окружность.

canvas->Brush->Style = bsClear;

canvas->Ellipse(FX - FR, FY - FR, FX + FR, FY + FR);

}

//---------------------------------------------------------------------------

// CEllise

//---------------------------------------------------------------------------

void CEllipse::Draw(TCanvas *canvas) const

{

// Делаем кисть пустой и рисуем эллипс.

canvas->Brush->Style = bsClear;

canvas->Ellipse(FX - FR, FY - FVR, FX + FR, FY + FVR);

}

//---------------------------------------------------------------------------

// CPolyline

//---------------------------------------------------------------------------

void CPolyline::Draw(TCanvas *canvas) const

{

unsigned c = Count();

// Если есть хотя бы один узел, то

if (c) {

// Определяем ссылку l на себя, для удобства обращения к операции индексации.

const CPolyline &l = *this;

// Если узлов больше одного, то изображаем их.

if (c > 1) {

// Перемещаем перо в первый узел.

canvas->MoveTo(l[0].X(), l[0].Y());

// Проводим линии в последующие.

for (unsigned i = 1; i < c; i++)

canvas->LineTo(l[i].X(), l[i].Y());

}

// Ставим точку в последнем узле.

// (Если узел один, то эта точка - вся ломаная.

// Если несколько, то точка требуется, т. к. LineTo последнюю точку не изображает).

const CPoint &p = l[c - 1];

canvas->Pixels[p.X()][p.Y()] = canvas->Pen->Color;

}

}

//---------------------------------------------------------------------------

// CFigureSet

//---------------------------------------------------------------------------

void CFigureSet::Draw(TCanvas *canvas) const

{

// Изображаем все фигуры множества.

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

// Draw через указатель на CFigure, то в случае не виртуальной функции Draw,

// вызвалась бы CFigure::Draw. Здесь же вызывается Draw из соответсвующего

// класса. Например, если укатель на CFigure реально хранит адрес CCircle, то

// вызывается CCircle::Draw и т. п.

for (Iterator i = First(); i != End(); i++)

(*i)->Draw(canvas);

}

//---------------------------------------------------------------------------

void CFigureSet::ColorDraw(TCanvas *canvas) const

{

// Изображаем все фигуры множества в цвете.

for (Iterator i = First(); i != End(); i++)

(*i)->ColorDraw(canvas);

}

//---------------------------------------------------------------------------

void CFigureSet::Add(CFigure &f)

{

// p - адрес добавляемой фигуры f.

CFigure *p = &f;

// Пресекаем попытку добавить в множество само множество.

if (this == p) return;

for (Iterator i = First(); i != End(); i++)

// Если указанная фигура уже добавлена, то выходим.

if (*i == p) return;

// Иначе, добавляем адрес фигуры в список.

PushBack(p);

}

//---------------------------------------------------------------------------

#pragma package(smart_init)

Листинг модуля OOPExample.h

//---------------------------------------------------------------------------

#ifndef OOPExampleH

#define OOPExampleH

//---------------------------------------------------------------------------

#include <Classes.hpp>

#include <Controls.hpp>

#include <StdCtrls.hpp>

#include <Forms.hpp>

#include <ExtCtrls.hpp>

#include <ComCtrls.hpp>

#include "Classes.h"

//---------------------------------------------------------------------------

class TfrmOOPExample : public TForm

{

__published: // IDE-managed Components

TButton *Create;

TCheckBox *Color;

TColorBox *ColorBox;

TPaintBox *PB;

TPanel *Panel1;

TPanel *Panel2;

TTreeView *TV;

TSplitter *splrMain;

TTimer *FlashTimer;

void __fastcall CreateClick(TObject *Sender);

void __fastcall ColorClick(TObject *Sender);

void __fastcall FormDestroy(TObject *Sender);

void __fastcall ColorBoxClick(TObject *Sender);

void __fastcall FormCreate(TObject *Sender);

void __fastcall PBPaint(TObject *Sender);

void __fastcall TVClick(TObject *Sender);

void __fastcall FlashTimerTimer(TObject *Sender);

private: // User declarations

// Множества выводимых фигур.

CFigureSet RandomSet, // случайно-генерируемое

FixedSet; // заданное

unsigned Flashs; // число миганий выбранной в TV фигуры, умноженное на 2

CFigure *Flash; // адрес мигающей фигуры

// Наполняет множество фигур s случайным числом случайно-сгенерированных фигур.

// Названия созданных фигур добавляются в ветку дерева node.

// recursive - условие, разрешающее генерировать множества фигур.

// Фигуры создаются операцией new.

void CreateFigures(CFigureSet &s, TTreeNode *node, bool recursive);

// Удаляет (операцией delete) фигуры, содержащиеся в множестве фигур s.

void FreeFigures(CFigureSet &s);

// Рисует указанную фигуру заданным в ColorBox цветом если переключатель

// Color включен; иначе фигура изображается ее собственным цветом.

void DrawFigure(CFigure &f);

public: // User declarations

__fastcall TfrmOOPExample(TComponent* Owner);

};

//---------------------------------------------------------------------------

extern PACKAGE TfrmOOPExample *frmOOPExample;

//---------------------------------------------------------------------------

#endif

Листинг модуля OOPExample.cpp

//---------------------------------------------------------------------------

#include <vcl.h>

#include <stdlib.h> // random и randomize

#include <typeinfo> // требуется для использования ключевого слова typeid

#pragma hdrstop

#include "OOPExample.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

#pragma resource "*.dfm"

TfrmOOPExample *frmOOPExample;

//---------------------------------------------------------------------------

// Конструктор

//---------------------------------------------------------------------------

__fastcall TfrmOOPExample::TfrmOOPExample(TComponent* Owner)

: TForm(Owner)

{

randomize();

// Статически определенные

static CPoint p(100, 100); // точку

static CCircle c(p, 10); // окружность

static CEllipse e(p, 20, 12); // эллипс

static CPoint coords[] = { // и ломаную

CPoint(p.X() - 30, p.Y() - 30),

CPoint(p.X() + 30, p.Y() - 30),

CPoint(p.X() + 30, p.Y() + 30),

CPoint(p.X() - 30, p.Y() + 30),

CPoint(p.X() - 30, p.Y() - 30)

};

static CPolyline pl(sizeof(coords) / sizeof(coords[0]), coords); // и ломаную

FixedSet = p, c, e, pl; // помещаем в множество фигур FixedSet.

// Статически определяем 4 окружности, и

static CCircle cs[4] = {

CCircle(coords[0], 5),

CCircle(coords[1], 5),

CCircle(coords[2], 5),

CCircle(coords[3], 5)

};

// Добавляем их к множеству FixedSet.

FixedSet += cs[0], cs[1], cs[2], cs[3];

}

//---------------------------------------------------------------------------

// Закрытые функции

//---------------------------------------------------------------------------

void TfrmOOPExample::CreateFigures(CFigureSet &s, TTreeNode *node, bool recursive)

{

// Массив возмножных цветов фигур.

static const TColor Colors[] = {

clRed, clBlue, clGreen, clLime, clAqua, clWhite, clYellow, clOlive

};

// Число возможных цветов (элементов в массиве цветов).

const NColors = sizeof(Colors) / sizeof(Colors[0]);

// Перечисление типов фигур.

enum { Point, Circle, Ellipse, Polyline, Set, NFigures };

// Массив имен фигур, соответсвующий перечислению.

static const char *Names[NFigures] = {

"Точка", "Окружность", "Эллипс", "Ломаная", "Множество фигур"

};

int n, c, x, y, N;

CFigure *f; // адрес генерируемой фигуры

TTreeNode *nn; // ветка дерева, соответсвующая генерируемой фигуре

c = random(11) + 10; // число генерируемых фигур

x = PB->ClientWidth; // ширина и

y = PB->ClientHeight; // высота области, внутри которой генерируются фигуры

N = NFigures - !recursive; // число генерируемых типов фигур

// (если recursive = true, то генерируются все типы фигур,

// иначе - все, кроме Set)

// Генерируем c фигур.

while (c--) {

// Выбираем тип фигуры.

n = random(N);

// Добавляем ветку дерева с заголовком, содеращим имя фигуры.

nn = TV->Items->AddChild(node, Names[n]);

// Генерируем случайным образом фигуру типа n.

switch (n) {

case Point:

f = new CPoint(random(x), random(y));

break;

case Circle:

f = new CCircle(random(x), random(y), random(25));

break;

case Ellipse:

f = new CEllipse(random(x), random(y), random(25), random(25));

break;

case Polyline:

{

// Для ломаной создаем случайным образом массив узлов:

// Число узлов 1..10.

PointArray coords(random(10) + 1);

// Первый узел - случайное положение.

coords[0] = CPoint(random(x), random(y));

// Остальные узлы - случайное положение в пределах +-25 пикселей по

// вертикали и горизонтали от предыдущего узла.

for (unsigned i = 1; i < coords.Count(); i++) {

coords[i].X(coords[i - 1].X() + (random(50) - 25));

coords[i].Y(coords[i - 1].Y() + (random(50) - 25));

}

// И саму ломаную.

f = new CPolyline(coords);

}

break;

case Set:

// Создаем объект класса множество фигур,

f = new CFigureSet();

// и рекурсивно вызываем CreateFigures для заполнения этого множества;

// дальнейшая рекурсия запрещена.

CreateFigures(*(CFigureSet *)f, nn, false);

break;

}

// Случайным образом выбираем цвет для созданной фигуры,

f->Color(Colors[random(NColors)]);

// и добаляем ее в заданное множество.

s.Add(*f);

// В ветку дерева с именем фигуры помещаем адрес самой фигуры.

nn->Data = f;

}

}

//---------------------------------------------------------------------------

void TfrmOOPExample::FreeFigures(CFigureSet &s)

{

CFigureSet::Iterator i;

// Для всех фигур множества s:

for (i = s.First(); i != s.End(); i++) {

// Берем адрес фигуры.

CFigure *f = *i;

// Механизм Runtime Type Identifiaction (RTTI) - определение типа во время выполнения,

// позволяет определить реальный тип объекта, на который указывает некоторый указатель

// на полиморфный класс.

// В данном случае используется операция typeid, которая возвращает константную ссылку на

// объект типа type_info, описывающий тип аргумента. Аргументом может быть либо тип данных,

// либо разыменованный указатель, либо ссылка.

// Для класса type_info определены операции равенства и неравентсва, что позволяет проверить,

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

// Если f указывает на множество фигур, то (до удаления самого множества) удаляем фигуры,

// содержащиеся в нем при помощи FreeFigures.

if (typeid(*f) == typeid(CFigureSet))

FreeFigures(*(CFigureSet *)f);

// Удаляем фигуру.

// Операция delete вызывает деструктор. Поскольку деструктор виртуальный, то

// для каждого класса-фигуры будет вызван свой деструктор, несмотря то, что

// f имеет тип CFigure*. В противном случае всегда вызывался бы деструктор класса CFigure.

delete f;

}

}

//---------------------------------------------------------------------------

void TfrmOOPExample::DrawFigure(CFigure &f)

{

TCanvas *c = PB->Canvas;

// Если переключатель Color включен, то

if (Color->Checked) {

// устанавливаем цвет пера на канве PB равным выбранному в ColorBox,

c->Pen->Color = ColorBox->Selected;

// и рисуем фигуру этим цветом.

f.Draw(c);

}

// Иначе

else

// изображаем фигуру ее собственным цветом.

f.ColorDraw(c);

}

//---------------------------------------------------------------------------

// События

//---------------------------------------------------------------------------

void __fastcall TfrmOOPExample::FormCreate(TObject *Sender)

{

// При создании формы создаем фигуры.

Create->Click();

}

//---------------------------------------------------------------------------

void __fastcall TfrmOOPExample::FormDestroy(TObject *Sender)

{

// При уничтожении формы удаляем созданные фигуры.

FreeFigures(RandomSet);

}

//---------------------------------------------------------------------------

void __fastcall TfrmOOPExample::PBPaint(TObject *Sender)

{

// Изображаем случайный и заданный наборы фигур.

DrawFigure(RandomSet);

FixedSet.ColorDraw(PB->Canvas);

}

//---------------------------------------------------------------------------

void __fastcall TfrmOOPExample::CreateClick(TObject *Sender)

{

// Отключаем мигание (см. ниже) выбранной фигуры (если включено).

FlashTimer->Enabled = false;

// Удаляем динамически созданные фигуры, которые помещены в множество RandomSet.

FreeFigures(RandomSet);

// Опустошаем это множество (удалаем указатели на уже несуществующие фигуры).

RandomSet.Clear();

// Очищаем дерево фигур,

TV->Items->Clear();

// добавляем в него корневую ветку,

TTreeNode *root = TV->Items->Add(NULL, "Случайные фигуры");

// соответствующую множеству фигур RandomSet.

root->Data = &RandomSet;

// Создаем фигуры.

CreateFigures(RandomSet, root, true);

// Раскрываем корневую ветку.

root->Expand(false);

// И рисуем созданные фигуры.

PB->Repaint();

}

//---------------------------------------------------------------------------

void __fastcall TfrmOOPExample::ColorClick(TObject *Sender)

{

// При переключении переключателя Color:

// Задаем видимость ColorBox-а и

ColorBox->Visible = Color->Checked;

// перерисовываем фигуры.

PB->Repaint();

}

//---------------------------------------------------------------------------

void __fastcall TfrmOOPExample::ColorBoxClick(TObject *Sender)

{

// При изменении цвета перерисовываем фигуры.

PB->Repaint();

}

//---------------------------------------------------------------------------

void __fastcall TfrmOOPExample::TVClick(TObject *Sender)

{

// При щелчке по дереву:

// Если какая-либо ветка дерева выбрана, то:

if (TV->Selected) {

// Берем адрес фигуры, которой соответствует ветка.

CFigure *f = (CFigure *)TV->Selected->Data;

// Если ветка действительно соответсвует некоторой фигуре, то:

if (f) {

// Если какя-то фигура уже мигает и стерта, то

if (FlashTimer->Enabled && !(Flashs % 2))

// изображаем ее.

DrawFigure(*Flash);

// Устанавливаем число счетчик мигания.

Flashs = 11;

// Устанваливаем фигуру, которая мигает.

Flash = f;

// Включаем таймер мигания.

FlashTimer->Enabled = true;

}

}

}

//---------------------------------------------------------------------------

void __fastcall TfrmOOPExample::FlashTimerTimer(TObject *Sender)

{

// Если уменьшенное значение счетчика мигания делится на 2, то

if (--Flashs % 2) {

// стираем фигуру.

PB->Canvas->Pen->Color = clBlack;

Flash->Draw(PB->Canvas);

}

// Иначе рисуем ее заново.

else

DrawFigure(*Flash);

// Если счетчик обнулился, выключаем мигание.

if (!Flashs)

FlashTimer->Enabled = false;

}

//---------------------------------------------------------------------------