Добавил:
sergeevpavel0406@mail.ru СОВА Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Информатика в техническом университете / Информатика в техническом университете. Объектно ориентированное программирование

.pdf
Скачиваний:
105
Добавлен:
06.03.2018
Размер:
9.48 Mб
Скачать

3. Средства ООП в Borland C++ 3.1

do {if( !BJn(GetEl(i))) DeI(GetEl(i)); else i-\-+;} while (i<sizetek);

return *this; }

template <class type> Tset<type>

& operator -(Tset<type> &A,Tset<type> &B)

{ Tset<type> '^C=new Tset<type>(A.Getsize()); for(inti=0;i<A.sizetek;i-\-+) if(!BJn(A.GetEl(i))) C'>Add(A.GetEl(i)); return *C; }

template <class type> Tset<type>

&operator *(Tset<type>&A,Tset<type> &B) {intl;

if (A. GetsizeO > B. GetsizeQ) l=A. GetsizeQ; else l=B. GetsizeQ; Tset<type> *C=new Tset<type>(l);

forftnt i=0;i<A.sizetek;i++)

 

{if(B.In(A.GetEl(i)))

C->Add(A.GetEl(i));}

return *C;

}

 

 

void mainQ

 

 

 

(int n; clrscrQ;

 

 

 

Tset<char> aa(15),bb(10),dd(10),cc;

 

cout«"Введите

число членов формируемого множества п<- ";

cout«aa.GetsizeO«endl;

cin»n;

a(LlnSet(n);

cout«*'Введите

число членов формируемого мноэюества п<= ";

cout«bb.GetsizeO«endl;

cin»n;

bb.InSet(n);

cout«"Введите

число членов формируемого мноэюества п<= ";

cout«dd.Getsize()«endl;

cin»n;

dd.InSet(n);

clrscrQ;

 

 

 

cout<< " Миоэюество aa "<<endl; aa. OutSetQ; cout< < " Множество bb "< <endl; bb. OutSetQ; cout< < " Множество dd "«endl; dd OutSetQ;

Tset<char> ee='aa*bb; II иншщализировать объект уже созданным

cout«"Пересечениемноэюеств

aa и bb"«endl; ее.OutSetQ;

aa. CrossSet(bb);

 

cout«"Пересечение мноэюеств aa и bb компонентное"«endl;

aaOutSetQ;

 

aa-aa+dd;

 

cout<< " Объединение множеств aa и dd"«endl;

aa. OutSetQ;

ccF^dd-bb;

 

cout< < "Дополнение мноэюеств dd и bb "< <endl;

cc. OutSetQ;

cc=dd; II явная операщм присвоения

 

cc.OutSetQ;

 

}

 

Тестирующая программа осуществляет формирование трех множеств букв и операции над ними, а также фиксирует последовательность и место вызова конструкторов различного назначения и деструкторов.

160

3.9.Контейнеры

3.9.Контейнеры

Решение многих задач подразумевает создание наборов объектов в различных формах и обработку таких наборов.

Объект, назначением которого является хранение объектов других типов

иуправление ими, принято называть контейнером. Классическими примерами контейнеров являются списки, вектора, ассоциативные массивы. Иногда содержимое контейнеров называют последовательностями.

ВC++ конггейнеры можно реализовать с использованием контейнерных

ипараметризованных классов.

Контейнерные классы. Данный термин обозначает распространенный прием разработки классов, использующий механизмы композиции или наполнения для подключения некоторых объектов к управляющему объекту - контейнеру. Контейнерный класс содержит в своем определении несколько объектных полей или полей - указателей на объекты. Если контейнерный класс использует механизм КОМПОЗРЩИИ, ТО ТИП И количество управляемых объектов жестко определены типом и количеством объектных полей. Если он использует механизм наполнения, то подключение реализуется через указатели, следовательно, контейнер может управлять как объектами некоторого базового, так и объектами всех потомков этого класса.

Обьгшо контейнерные классы реализуют некоторые типовые структуры, такие как массив, стек или список и типовые операции над данными, которые могут быть записаьпэ! в эти структуры или прочитаны из них.

Основная операция любого контейнерного класса - последовательный просмотр объектов. Такая обработка обеспечивается двумя способами.

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

Пример 3.38. Контейнерный класс с процедурой поэлементной обработки. Пусть требуется разработать контейнер на базе сортированного списка элементов, принадлежащих иерархии классов Число - Строка - Таблица.

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

161

3. Средства ООП в Borland C++ 5.7

предыдущего элемента списка. Эти два класса образуют контейнерный класс, управляемые объекты которого должны наследоваться от класса Элемент (рис. 3.6).

Затем на базе этих классов разработаем абстрактный контейнерный класс Сортированный список, предусматривающий метод сортировки Sort с внутренним вызовом метода сравнения элементов Compare. Наличие внутреннего метода сравнения позволит в дальнейшем на базе этого класса создавать другие классы, использующие различные законы сортировки.

От класса Элемент наследуем классы предметной области задачи: Число, Строка, Таблица, добавляя новые поля и перекрывая метод печати содержимого элемента Print.

Теперь можно описать класс Пользовательский сортированный список, который перекроет метод сравнения элементов при сортировке, задав сравнение реальных полей классов предметной области задачи.

Ниже представлена программа тестирующая разработанную иерархию классов:

^include <stdio.h>

 

#include <conio.h>

 

class TElement

II абстрактный класс Элемент списка

{public:

 

TElement *pre, *suc; /* Два поля - ссылка на предьщущий и ссылка на последующий элементы */

virtual void Print(void) =0;

II абстрактная фушащя печати

TElement(void) fpre=suc=NULL;}

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

virtual -TElementfvoid)

II деструктор

{puts("yHU4moDfcumb элемент,");}

};

class TSpisok II класс Список

{ protected: TElement *first, Hast, *cur; I* поля - указатели соответственно

Методы: Print

на первый, последний и текущий элементы списка */

И^1|||— и

iBiilii!

first, last, cur

Поля: pre, sue

 

 

Поля:

Поля: num

1

 

Методы:

Число

 

Add, Del,

Методы: Print

 

ForEach

Поля: st[40]

1

Сортированный

Методы:

Строка

список

Sort, Compare

Методы: Print

 

 

 

Поля: str[20]

1

Пользовательский

Методы:

 

Таблица

сортированный

Compare

Методы: Print

список

Рис. 3.6. Иерархия классов для примера 3.38

162

 

3.9. Контейнеры

public:

 

 

void Add(TElement *e);

II добавление элемента в список

TElement *Del(void);

II удаление элемента из списка

void ForEachfvoid ("^fi(TElement *e)); /*выполнить для каждого

 

 

элемента */

TSpisok(void)

{first^last^cur^NULL;}

virtual ^TSpisokOfputsC*yHU4mo:M€umh список.'');}

};

 

 

void TSpisok: :Add(TElement "^e)

 

{if(first==NULL)

first=last=e;

 

else { e->suc=first; first=first'>pre=e;} } TElement *TSpisok::DeI(void)

{TElement *temp; temp=last;

if(last!=NULL)

{last=last->pre;

if(last!=NULL)last'>suc=NULL;}

if(last==NULL)first=NULL;

 

 

 

 

return temp;

 

}

 

 

 

 

 

void TSpisok::ForEach(void (''^fi(TElement "^e))

 

 

{cur=first;

 

 

 

 

 

 

 

 

while (cur!=NULL)

{(*f)(cur);

cur=cur->suc;}

}

 

class TSortSpisok:public TSpisok II класс Сортированный список

{protected:

 

 

 

 

 

 

 

 

virtual int Compare(void *opl,void

*op2)=0; /* абстрактная функция

public:

 

 

 

сравне1шя для процедуры сортировки */

 

 

 

 

 

 

 

 

void Sort(void);

 

 

II процедура сортировки

TSortSpisok(void):TSpisokO{} //конструктор

 

 

'^TSortSpisokQ

 

 

II деструктор

 

 

{putsC'yHU4Ho:Hcumb сортированный список.");}

};

 

 

 

 

 

 

 

 

void TSortSpisok: :Sort(void)

 

 

 

 

{int swap=l;

TElement "^temp;

 

 

 

while (swap)

 

 

 

 

 

 

 

{swap=0;

 

 

 

 

 

 

 

cur=first;

 

 

 

 

 

 

 

while

(сиг'>sue!-NULL)

 

 

 

 

{if

(Compare(cur,cur'>suc))

 

 

 

{temp=cur'>suc; cur'>suc=temp'>suc;

temp->suc=cur;

 

if (cur->pre!-NULL)

cur->pre->suc-temp;

else first-temp;

 

temp->pre=сиГ'>pre; cur->pre=temp;

 

 

if(cur->suc!=NULL)

cur'>suc->pre=cur;

else last=cur;

 

cur-temp;

 

 

 

 

 

 

swap=l;

}

 

 

 

 

163

3. Средства ООП в Borland С+ +3.1

else cur=cur->suc;

}

 

}

 

 

 

 

 

}

 

 

 

 

 

Mnclude <string.h>

 

 

 

 

class TNum: public

Telement II класс Число

 

{public:

 

 

 

 

 

int num;

 

II числовое поле целого типа

 

virtual voidPrint(void)

{printfC %d " ,num);}

 

TNumQ {}

 

 

 

II конструктор no умолчанию

TNum(int n):num(n) {}

 

II конструктор

 

^TNum(void)

{putsC Уиичтоэюить число.");}//деструктор

};

 

 

 

 

 

class TStr: public

TNum II класс Строка (в поле num - длина строки)

{public:

 

 

 

 

 

char St[40];

II поле символьная строка

 

virtual voidPrint(void)

 

{ TNum::PrintO; printfC ^^sW

,st);}

TStrQ {}

 

II конструктор no умолчанию

 

TStr(char

*s):TNum(strlen(s)){

 

strcpy(st,s);

 

 

 

if(num>=40)st[40]='\0';

 

elsest[num+l]=W;}

 

--TStr(void) {puts(" Уничтоэюитъ строку.");}

 

};

 

TStr II класс Таблица (добавляет строку)

class ТТаЫ: public

{public:

 

 

 

 

 

charstr[20];

 

 

 

 

virtual void Print(void) { TStr::PrintO;printfC %s\n "

,str);}

TTablQ {}

II конструктор no умолчанию

 

TTabl(char *s,char *s2):TStr(s){

 

strcpy(str,s2);

 

 

 

if(strlen(s2)>=20)strf20J=W;

 

else

str[strlen(s2)+lj='\0';}

 

^TTabl(void) {puts(" Уничтоэюитъ таблищ*');} //деструктор

};

class TSSpisok:public TSortSpisok I* класс Пользовательский сортированный список*/

{ protected:

virtual int Compare(void *opljVoid *op2) I* функция сравнения для

процедуры сортировки с явным преобразованием типа */

{return (((ТТаЫ *)ор1)->пит)<(((ТТаЫ *)ор2)'>пит);} public:

TSSpisok(void):TSortSpisokOO '^TSSpisok(void){putsC' Уничтожить с/список.");}

};

164

3.9. Контейнеры

void Show(TElement *e) //процедура для передачи в процедуру ForEach

{e'>PrmtO;}

TSSpisok N; II объект класса Сортированный список

void main(void)

 

 

 

{intk; charstr[40];

char str2[20];

 

TElement *p; II указатель на базовый класс Telement

// цикл формирования списка из объектов классов ТМшп, TStr, ТТаЫ

while(printfrВведите

число:"),scanfC %d",&k)!=EOF)

{p=new TNum(k);

 

 

N.Add(p);

 

 

 

printfCВведите строку:");scanf(" %s" ,str);

printfCВведите

строку 2:");scanfC' %s",str2);

p=new TTabl(str,str2);

 

NAdd(p);

 

 

 

printfCВведите

строку:");scanfC' %s",str);

p=new TStr(str);

 

 

N,Add(p);

}

 

 

putsC \пВведены элементы:");

 

N.ForEach(Show);

II вывести элементы списка

N.SortQ;

 

II сортировать

 

puts(" \пПосле сортировки:");

 

N.ForEach(Show);

getchQ;

 

puts(" \пУдаление чисел");

 

while ((p=N.DelO)!=NULL) {p->PrintO;

delete(p);}

puts(" \пКонец.");}

 

В т о р о й

с п о с о б вьшолнения поэлементной обработки реализуется

через определение итератора или класса итераторов, подходящего для данного вида контейнера. При помощи итераторов программист может управлять контейнером, не зная фактических типов, используемых для идентификации элементов. Несколько ключевых компонентных функций позволяют программисту найти концы последовательности элементов (раздел 1.7),

Использование шаблонов классов для проектирования контейнеров.

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

Пример 3.39. Контейнер на основе шаблона.

Рассмотрим программу, использующую шаблон Динамический массив для организации динамического массива, хранящего объекты классов Число и Строка (рис. 3.7).

165

 

3. Средства ООП в Borland C++ 3.1

 

 

 

 

Динамический

Поля: *contens,

 

 

 

массив

size, n

 

 

 

 

Методы: arrayob,

 

 

 

 

-arrayob,

Поле: num

Число

^

Динамический

operator[ ],

Метод: Print

массив

add,

 

 

 

 

sizeofitnas

Поле: *st

Строка

Метод: Print

 

 

Рис. 3.7. Иерархия классов для примера 3.39

#include <string.h> #include <iostream.h> ^include <stdlib.h> Mnclude <conio.h>

template < class type> II объявление шаблона класса с параметром type

class arrayob

II начало описания класса с именем arrayob

 

{type **contents; II массив указателей на объекты типа type

int size;

II максимальное количество объектов в массиве

int п;

II реальное количество объектов в массиве

public:

 

 

 

 

arrayob(int number) {contents =new type * [size=number];}

^arrayob Q;

 

 

 

 

voidadd(type

*p)

 

 

 

{ if(n==size)cerr«"

выход за пределы"; II добавление элементов

else {contents[п]-р;

п++;} }

 

type & operator [] (intx)

II

итератор массива объектов

 

{if ((x<0)\\(x>=size)) {cerr

«*'ошибочный индекс

"«x«endl;x=0;}

return *contents[xJ;}

 

 

 

int sizeofmasQ {return n;}

II возвращает реальный размер массива

};

template < class type> arrayob< type >::^arrayob Q II описание деструктора

{for(int i=0;i<size;i++) delete

contents[i];}

 

class TNum

II класс Число

 

{public:

int num;

 

 

 

virtual void Print(void) { cout<<num« " ";}

TNum(){cout«"Введите

4Ucno"«endl;

cin»num;}

TNum(intn):num(n)

{}

 

 

virtual--TNum(void)

{соШ«"Уничтоэ/сить

4ucno."«endl;}

} :

 

 

 

 

class TStr:public TNum II класс Строка

 

{public:

char "^st;

 

 

 

166

 

3.9. Контейнеры

 

virtual void Print(void) {TNum::Print(); cout«st«

" ";}

TStrQ;

II конструктор no умолчанию

TStr(char *s):TNum(strIen(s)) II конструктор с параметрами

{st=new charfnum+lj;strcpy(st,s); stfnumJ=W;

}

virtual--TStr(void) {соШ«"Уничтоэюитъ строку,"; delete []st;}

};

 

 

 

TStr::TStrO:TNum(40)

 

 

 

{ cout<< "введите строку"<<endl;

 

 

st=new char[num+lj;

cin»st;

 

 

num-strlen(st); st[num+lj='\0\'

}

 

arrayob<TNum> ob_a(5); /* массив из 5 указателей на объекты иерархии

voidmainQ

ТМшп*/

 

 

{ inti;

 

 

for(i=0;i<5;i^'^)

II поместить 5 объектов

if(i/2'^2==i)

obj2.add(new

TNum); II поместить Число

else ob_a.add(new TStr);

II поместить Строку

cout< < " содерэюимое контейнера "< < Ъ V

for (i=0;i<ob_a,sizeofinasO;i++)

oba[i].PrintQ;

getchO;}

 

 

Шаблон оперирует с указателями на объекты иерархии. Для вызовов методов Print и деструкторов классов иерархии используем механизм сложного полиморфизма.

Вопросы для самопроверки

1. Что такое класс в С+-ь? Какие существуют способы ограничения доступа к компонентам класса? Как и где они используются? Чем отличается описание компонентных функций внутри

ивне определения класса?

2.Сформулируйте особенности конструкторов и деструкторов классов C++? Что такое неинициилизирующий конструктор и как он отличается от конструктора без параметров? Когда использование неинициализирующего конструктора необходимо?

3.Что такое копирующий конструктор? Назовите случаи, когда использование такого конструктора необходимо.

4.Как описывается производный класс? Что такое множественное и виртуальное наследование?

5.Как определяется доступность компонент базового класса в производном классе? Какова последовательность подключения конструкторов и деструкторов базового и производного классов?

6.Назовнгге вццы полиморфизма в C++. Определите понятие виртуальных и абстрактных функций. Что такое абстрактный класс? Назовиге особенности использования абстрактного класса.

7.Что такое дружественные функции и дружественные классы? Как определить дружественные функции? Где и как они используются?

167

3.Средства ООП в Borland C++ 3.1

8.Что такое переопределение операций? Какие операции можно переопределять? Определите понятие функции-оператора. Чем отличаются компонентные и внешние функцииоператоры?

9.Какие сложности возникают при работе с динамическими объектами? Что такое виртуальный деструктор и каковы особенности его использования?

10.Что такое шаблон? Определите понятие шаблона функции и шаблона класса. Приведите примеры применения шаблонов классов.

11.Сопоставьте понятия «параметризованные» и «контейнерные» классы? Чем определяется выбор того или иного типа классов в конкретном случае?

12.Составьте программу, обеспечивающую работу со структурой данных типа стек.

4. СОЗДАНИЕ ПРИЛОЖЕНИЙ WINDOWS

Создание программных средств для семейства операционных систем Win32 имеет целый ряд особенностей, без знакомства с которыми невозможно рассмотрение объектных моделей Delphi и С+-»- Builder. Прежде всего прилож:ение Windows взаимодействует с пользователем и с операционной системой путем отправки и получения сообщений, поэтому основу любого прилож^ения составляет цикл обработки сообщений. Сообщения, полученные прилож^ением, диспетчируются и передаются для обработки так называемым оконным функциям, которые обеспечивают выполнение требуемых действий. Такое построение обработки в программе получило название событийного программирования. Кроме того, как правило, среды, в которых разрабатываются прилож^ения Windows, используют визуальную технологиюразработк интерфейсов.

4.1. Семейство операционных систем Windows с точки зрения программиста

Операционная система MS DOS предоставляла программисту минимальный набор средств. Она включала файловую систему, систему управления памятью, систему управления процессами и незначительный набор сервисных функций, облегчающих, например, вьшолнение операций вводавьюода в простейших случаях. Вместе с этим, будучи однопрограммной и, соответственно, предоставляя программе все ресурсы системы в монопольном режиме, MS DOS позволяла программисту «напрямую», минуя операционную систему, управлять техническими средствами (рис. 4.1). Причем, при прямом управлении во многих случаях можно было получить существенно большее быстродействие при меньших затратах памяти. В результате достаточно часто программы MS DOS используют прямое управление устройствами. Например, большинство графических программ MS DOS напрямую работают с видеопамятью, причем часть из них также самостоятельно поддерживает клавиатуру и мьппь.

Программирование на уровне аппаратных средств помимо достоинств имеет и существенные недостатки. Программа, неЦосредственно управляющая устройствами:

1) монопольно использует устройства и потому не может вьшолняться в мультипрограммной среде;

169