- •Оглавление
- •1 Основные методы программирования. Лекция №2 5
- •2 Методы объектно-ориентированного программирования. Лекция №6. 15
- •2.4 Вопросы по теме 54
- •3 Объектно-ориентированное проектирование. Лекция №12 55
- •3.4 Вопросы по теме 74
- •Введение. Лекция №1.
- •1Основные методы программирования. Лекция №2
- •1.1Процедурное программирование Методы процедурного программирования
- •Этапы разработки программ. Лекция 3.
- •1.2Объектно-ориентированное программирование Лекция №4
- •Этапы разработки оо программ. Лекция №5
- •Конструкторы и деструкторы. Лекция №7.
- •Друзья класса
- •2.2Наследование. Лекция №8
- •Производный класс
- •Простое наследование
- •Множественное наследование. Лекция №9
- •Виртуальные базовые классы
- •2.3Полиморфизм. Лекция №10
- •Перегрузка операций
- •Преобразования типов, определяемые классом
- •Перегрузка функций.
- •Чистый полиморфизм
- •Виртуальные элементы-функции
- •Абстрактные классы. Лекция №11.
- •Параметрический полиморфизм
- •Шаблоны функций
- •Шаблоны классов
- •2.4Вопросы по теме
- •3Объектно-ориентированное проектирование. Лекция №12
- •3.1Концепция объектно-ориентированного проектирования
- •3.2Проектирование структурной схемы программы Составление начальной иерархии и структуры классов
- •Реорганизация иерархии и структуры классов
- •Организационная структура программы
- •3.3Проектирование файлов интерфейсов классов. Лекция № 13. Файл интерфейса базового класса
- •Файлы интерфейсов производных классов
- •Файл управления
- •3.4Вопросы по теме
- •Общие моменты разработки программы на основе объектно-ориентированного подхода
- •Приложение а
Приложение а
(рекомендуемое)
Листинг модуля 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;
}
//---------------------------------------------------------------------------