cpp-oop (823968), страница 13
Текст из файла (страница 13)
Если он применяет механизм наполнения, то подключение реализуется черезуказатели, следовательно, контейнер может управлять как объектами некоторогобазового, так и объектами всех потомков этого класса.Обычно контейнерные классы реализуют типовые структуры, такие, как массив, стекили список, и включают методы, выполняющие основные операции над хранимымиданными.Обязательная операция, присутствующая в любом контейнерном классе –последовательная обработка объектов. Такая обработка может обеспечиваться двумяспособами.П е р в ы й с п о с о б базируется на создании специальной процедуры просмотравсех элементов контейнера. В эту процедуру в качестве параметра передается имяфункции или процедуры, реализующей алгоритм требуемой обработки элементаконтейнера.В т о р о йс п о с о б реализуется через определение итератора или классаитераторов, подходящего для данного вида контейнера.
При помощи итераторовпрограммист может управлять контейнером, не зная фактических типов элементов.Несколько ключевых компонентных функций позволяют программисту найти концыпоследовательности элементов.Пример 3.6. Контейнерный класс с процедурой поэлементной обработки. Пустьтребуетсяразработатьконтейнернабазесортированногоспискаэлементов,принадлежащих иерархии классов Число – Строка – Таблица.Структуру классов будем разрабатывать поэтапно. В основу иерархии классовположим классы Список – Элемент. Эти два класса образуют контейнерный класс,ОглавлениеИванова Г.С., Ничушкина Т.Н. «Объектно-ориентированное программирование на языке C++ в среде Microsoft Visual Studio 2008»90управляемые объекты которого должны наследоваться от класса Элемент (рис.
3.1). КлассСписок будет содержать три поля – указатели на объекты класса Элемент: указатель напервый элемент, указатель на последний элемент и указатель на текущий элемент. Этиуказатели используются для организации двусвязного списка. Класс Элемент содержиттолько два поля – указатели на элементы того же класса, которые будут хранить адресследующего и адрес предыдущего элементов списка.TElementTSpisokpre, sucTElement()virtual Print()TElement *first, *last, *curTSpisok()~TSpisok()Add(TElement *e)Del()ForEach(void (*f)(TElement *e))TNumint numTNum()virtual Print()TSortSpisokTSortSpisok()Sort()virtual Compare(void *p1,void *p2)TStrchar st[40]TStr()virtual Print()TSSortSpisokTTablTSSortSpisok()virtual Compare(void *p1,void *p2)char str[20]TTable()virtual Print()Рис.
3.1. Диаграмма классов, реализуемая в примере 3.6Затем на базе этих классов разработаем абстрактный контейнерный классСортированный список, предусматривающий метод сортировки Sort с внутреннимвызовом метода сравнения элементов Compare. Наличие внутреннего метода сравненияпозволит в дальнейшем на базе этого класса создавать другие классы, использующиеразличные законы сортировки.От класса Элемент наследуем классы Число, Строка, Таблица, добавляя новые поляи перекрывая метод вывода содержимого элемента Print().Теперь можно описать класс Пользовательский сортированный список, которыйпереопределит метод сравнения элементов при сортировке, задав сравнение реальныхполей классов предметной области задачи.#include <locale.h>#include <stdio.h>#include <conio.h>ОглавлениеИванова Г.С., Ничушкина Т.Н. «Объектно-ориентированное программирование на языке C++ в среде Microsoft Visual Studio 2008»91#include <string.h>// абстрактный класс Элемент спискаclass TElement{public: TElement *pre,*suc;TElement(void) { pre=suc=NULL;}virtual void Print(void)=0;// конструктор// абстрактный метод вывода};// класс Cписокclass TSpisok{protected: TElement *first,*last,*cur; /*указатели на первый,последний и текущий элементы списка */public:TSpisok(void){ first=last=cur=NULL;}~TSpisok();// деструкторvoid Add(TElement *e); // добавление элемента в списокTElement *Del(void);// удаление элемента из спискаvoid ForEach(void (*f)(TElement *e)); // поэлементная обработка};TSpisok::~TSpisok(){while ((cur=Del())!=NULL){ cur->Print();delete(cur); }}void TSpisok::Add(TElement *e){if (first==NULL)else {first=last=e;e->suc=first; first=first->pre=e;}}TElement *TSpisok::Del(void){TElement *temp=last;if (last!=NULL){last=last->pre;if (last!=NULL) last->suc=NULL;ОглавлениеИванова Г.С., Ничушкина Т.Н.
«Объектно-ориентированное программирование на языке C++ в среде Microsoft Visual Studio 2008»92}if (last==NULL) first=NULL;return temp;}void TSpisok::ForEach(void (*f)(TElement *e)){cur=first;while (cur!=NULL) { (*f)(cur); cur=cur->suc;}}class TSortSpisok:public TSpisok // класс Cортированный список{protected:virtual int Compare(void *op1,void *op2)=0; /* абстрактный методсравнения для процедуры сортировки */public:// метод сортировкиvoid Sort(void);// конструкторTSortSpisok(void):TSpisok(){}// деструктор~TSortSpisok(){}};void TSortSpisok::Sort(void){int swap=1;TElement *temp;while (swap){swap=0;cur=first;while (cur->suc!=NULL){if (Compare(cur,cur->suc)){temp=cur->suc;cur->suc=temp->suc;temp->suc=cur;if (cur->pre!=NULL)cur->pre->suc=temp;ОглавлениеИванова Г.С., Ничушкина Т.Н.
«Объектно-ориентированное программирование на языке C++ в среде Microsoft Visual Studio 2008»93else first=temp;temp->pre=cur->pre;cur->pre=temp;if (cur->suc!=NULL)cur->suc->pre=cur;else last=cur;cur=temp; swap=1;}else cur=cur->suc;}}}class TNum: public TElement// класс Число{public:// числовое поле целого типаint num;void Print(void) { printf("%d ",num); }TNum(){}// конструктор по умолчаниюTNum(int n):num(n) {}// конструктор};class TStr: public TNum// класс Строка (num - длина строки){public:char st[40];TStr(){}// поле символьная строка// конструктор по умолчаниюTStr(char *s):TNum(strlen(s)){strcpy(st,s);if (num>=40)st[40]='\0'; else st[num+1]='\0';}void Print(void){TNum::Print(); printf("%s\n",st);}};class TTabl: public TStr // класс Таблица - добавляет строку{public:char str[20];ОглавлениеИванова Г.С., Ничушкина Т.Н. «Объектно-ориентированное программирование на языке C++ в среде Microsoft Visual Studio 2008»94void Print(void){TStr::Print();TTabl(){}printf("%s\n",str); }// конструктор по умолчаниюTTabl(char *s,char *s2):TStr(s){strcpy(str,s2);if (strlen(s2)>=20) str[20]='\0';else str[strlen(s2)+1]='\0';}};class TSSpisok:public TSortSpisok /* класс Пользовательскийсортированный список*/{protected:int Compare(void *op1,void *op2) /* метод сравнениядля процедуры сортировки с явным преобразованием типа */{ return(((TTabl *)op1)->num)<(((TTabl *)op2)->num);}public:TSSpisok(void):TSortSpisok(){}~TSSpisok(void){}};void Show(TElement *e){// процедура для передачи в метод ForEache->Print(); }TSSpisok N;// объект класса Сортированный списокvoid main(){setlocale(0,"russian");int k;char str[40];char str2[20];TElement *p; // указатель на базовый класс Telement// цикл формирования списка из объектов классов TNum, TStr, TTablwhile ( printf("Введите число:"),scanf("%d",&k)!=EOF){p=new TNum(k);N.Add(p);printf("Введите строку:");scanf("%s",str);ОглавлениеИванова Г.С., Ничушкина Т.Н.
«Объектно-ориентированное программирование на языке C++ в среде Microsoft Visual Studio 2008»95printf("Введите строку 2:"); scanf("%s",str2);p=new TTabl(str,str2);N.Add(p);printf("Введите строку:");scanf("%s",str);p=new TStr(str);N.Add(p);}puts("\nВведены элементы:");N.ForEach(Show);// вывод элементов спискаN.Sort();// сортировкаputs("\nПосле сортировки:");N.ForEach(Show);_getch();}Пример 3.7.
Контейнерный класс с итераторами. Для демонстрации работы ситераторами воспользуемся текстом примера 3.6, изменив его следующим образом: изкласса TSpisok удалим метод ForEach() и добавим два метода-итератора ifirst() иinext(). Описание остальных классов оставим без изменения.В результате класс TSpisok будет иметь видclass TSpisok// класс Список{protected: TElement *first,*last,*cur;public:TSpisok(void){ first=last=cur=NULL;} // конструктор~TSpisok();// деструкторvoid Add(TElement *e); // добавление элемента в списокTElement *Del(void);// удаление элемента из списка// функции-итераторыTElement * ifirst(){return cur=first; }TElement * inext(){// указатель на первый элемент// указатель на следующий элементreturn cur=cur->suc; }};ОглавлениеИванова Г.С., Ничушкина Т.Н.
«Объектно-ориентированное программирование на языке C++ в среде Microsoft Visual Studio 2008»96Фрагмент программы, демонстрирующий применение итераторов для такогоописания класса, будет выглядеть следующим образом:TSpisok N;// объявление объекта класса Списокvoid main(void){int k;char str[40];char str2[20];TElement *p;// цикл формирования списка из элементов классов TNum, TStr, TTablwhile(printf("Введите число:"), scanf("%d",&k)!=EOF){p=new TNum(k);N.Add(p);printf("Введите строку:");scanf("%s",str);printf("Введите строку 2:");scanf("%s",str2);p=new TTabl(str,str2);N.Add(p);printf("Введите строку:");scanf("%s",str);p=new TStr(str);N.Add(p);}puts("\nВведены элементы:");// цикл обработки каждого элемента списка с помощью итераторовp=N.ifirst();while (p!=NULL){p->Print();p=N.inext();}_getch();}ОглавлениеИванова Г.С., Ничушкина Т.Н.
«Объектно-ориентированное программирование на языке C++ в среде Microsoft Visual Studio 2008»97Использование шаблонов классов для проектирования контейнеров. Оченьчасто для реализации контейнера используются параметризованные классы, в том числе истандартные, описанные в библиотеке CLASSLIB. Они также могут работать с объектамиодного класса или иерархии классов. В последнем случае в качестве параметра в шаблонпередается имя класса-родителя или указатель на класс-родитель. Пример контейнера,построенного на основе шаблона, приведен в разделе 6.3.ОглавлениеИванова Г.С., Ничушкина Т.Н. «Объектно-ориентированное программирование на языке C++ в среде Microsoft Visual Studio 2008»98Вопросы для самоконтроля1. Какое отношение между классами называется композицией? Для чего онаиспользуются?Ответ.2.
Чем композиция отличается от наполнения? Как наполнение реализуется?Ответ.3. Что такое виртуальный деструктор и каковы особенности его использования?Ответ.4. Что такое восходящее и низходящее приведение типов? Где и как онииспользуются?Ответ.5. Что называют "контейнером" в программировании? Для чего используютконтейнеры?Ответ.ОглавлениеИванова Г.С., Ничушкина Т.Н.
«Объектно-ориентированное программирование на языке C++ в среде Microsoft Visual Studio 2008»994Особые случаи организации доступа к объектам и их компонентам4.1Локальные и вложенные классыКласс может быть объявлен внутри некоторой функции. Такой класс в С++ принятоназывать локальным. Функция, в которой объявлен локальный класс, не имеетнепосредственного доступа к компонентам локального класса, доступ осуществляется суказанием имени объекта встроенного класса.
Локальный класс не может иметьстатических полей. Объект локального класса может быть создан только внутри функции,в области действия объявления класса. Все компонентные функции локального классадолжны быть встраиваемыми.Иногда возникает необходимость объявления одного класса внутри другого.