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

Основы объектно-ориентированного программирования задач на графах. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
7.4. Определение чистых виртуальных функций в классе List
71
Аналогично определим и чистые виртуальные функции out-
put_graph(), adjacent(). Добавьте в описание класса Matr кон-
структор, который создаст двумерный массив в динамической памяти, и деструктор для удаления масива из динамической памяти. Получим следу­ющее описание класса Matr:
сlass Matr : public Graph
{ protected: bool **matr; public:
void input_graph(); void output_graph();
int adjacent(int,int); Matr(int);
~Matr(); };
Как реализовать все эти функции, мы уже рассматривали в предыду-
щих разделах и останавливаться на этом не будем.
7.4. Определение чистых виртуальных функций в классе List
Рассмотрим создание класса List, который должен дополнить базо-
вый класс Graph операциями над графами, заданными в списочной форме.
Во втором разделе рассмотрено представление в памяти графов, за-
данных в списочной форме, двумя одномерными массивами: массивом раз­делителей и основным массивом для хранения списков номеров смежных вершин (см. рис. 5). Ниже будет рассмотрено представление списков смеж­ных вершин массивом указателей и одномерными массивами разной раз­мерности. Такая форма в С++ более приемлема при использовании дина­мической памяти.
Определим в классе List собственное данное – указатель на массив
указателей:
int **list;
7. Иерархии классов для решения задач на графах
72
Определим чистую виртуальную функцию input_graph() для
ввода графов, заданных в списочной форме. Для этого добавим в описание класса List прототип этой функции (см. рис. 21):
void input_graph();
Затем определим эту функцию вне описания класса List:
//------- функция задания списка ---------------------
void List :: input_graph() { int * mas = new int [N]; list = new int * [N]; int k,i,j; for( i=0; i<N; i++) { k=0; cout<<"Tops, adjacent of top "<<i+1<<endl; while(getch()!=27) cin >> mas[k++]; list[i]=new int [k+1]; *list[i]=k;
for( j=0;j<*list[i];j++) *(list[i]+j+1)=mas[j]; } delete[] mas; } //----------------------------------------------------
Рассмотрим работу этой функции. Так как неизвестно заранее коли­чество ребер графа, то выделить динамическую память с помощью кон­структора класса List не удастся. Конструктор класса List
List (int n1):Graph(n1){}
должен только вызвать конструктор базового класса Graph и передать ему значение числа вершин графа.
7.4. Определение чистых виртуальных функций в классе List
73
Вернемся к нашей функции. Оператор
int * mas = new int [N];
создаст в динамической памяти вспомогательный массив mas, в который будут помещаться номера вершин, смежных с какой-то одной вершиной.
В общем случае его размерность должна быть равна количеству вер-
шин в графе. Следующий оператор:
list = new int * [N];
выделит динамическую память для массива указателей на одномерные мас­сивы, соответствующие спискам вершин, смежных с каждой из вершин графа. Таких списков столько, сколько вершин в графе. Поэтому размер­ность массива указателей тоже N. В цикле
for( i=0; i<N; i++) { . . . }
переменная k выполняет роль счетчика количества вершин, которые смежны вершине с номером i+1. Для каждой вершины с номером i+1 цикл
while(getch()!=27)
cin>>mas[k++];
будет вводить в элементы вспомогательного массива mas номера вершин с ней смежных до тех пор, пока не будет нажата кнопка <Esc>. Затем опера­тор
list[i]=new int [k+1];
выделит в динамической памяти столько байт для одномерного массива, сколько необходимо для хранения k+1 целых чисел.
Первому элементу этого массива оператором
*list[i]=k;
7. Иерархии классов для решения задач на графах
74
будет присвоено значение количества вершин, смежных с вершиной i+1, а операторы
for(j=0;j<*list[i];j++)
*(list[i]+j+1)=mas[j];
перепишут в элементы этого массива, начиная со второго, введенные номе­ра вершин. После выполнения цикла оператор
delete[] mas;
удалит вспомогательный массив из динамической памяти.
Таким образом, видно, что реализация виртуальной функции
input_graph() в классе List совершенно отличается от ее реализации в классе Matr.
Теперь перегрузим в классе List чистые витруальные функции output_graph() и adjacent(). Для этого добавьте в описание клас- са List прототипы этих функций:
void output_graph();
int adjacent(int,int);
Определение этих функций сделайте вне описания класса List:
//------- функция вывода списка ----------------------
void List :: output_graph() { int i,j; cout<<" Out List";
for( i=0; i<N; i++) { cout<< endl<< i+1<< ": "; for( j=1; j<=*list[i]; j++) cout<< *(list[i]+j)<< ","; } }
7.4. Определение чистых виртуальных функций в классе List
75
//----- функция проверки смежности двух вершин ------­int List :: adjacent(int x1,int x2) { for(int j=1; j<=*list[x1-1]; j++) if(*(list[x1-1]+j)==x2) return 1; return 0; } //----------------------------------------------------
Функция output_graph() для каждой вершины с номером i+1
просматривает элементы массива с указателем list[i], соответствую­щие списку смежных с ней вершин графа, и выводит их значения на экран. Первый элемент с адресом list[i] пропускается. Его значение определяет границу просмотра.
В функции adjacent() просматриваются элементы массива смежных
вершин только для одной вершины с номером x1. Если какой-то элемент окажется равным значению второго аргумента x2, то функция завершит работу и вернет значение, равное 1.
Если массив будет просмотрен до конца и функция не завершит ра-
боту, то это будет означать, что вершины с номером x2 в списке смежных вершин для вершины x1 нет. В этом случае функция вернет значение, рав­ное 0.
Теперь осталось добавить в описание класса деструктор, который
удалит из динамической памяти массивы, соответствующие спискам смеж­ных вершин, и массив указателей:
//------- деструктор класса List --------------------­List::~List() { if(list!=NULL) { for(int i=0; i<N; i++) delete[] list[i]; delete[] list; } } //----------------------------------------------------
7. Иерархии классов для решения задач на графах
76
Окончательно описание класса List будет таким:
//---------------- класса List -----------------------­class List : public Graph { protected: int **list; public: void input_graph(); void output_graph(); int adjacent(int,int); List (int n1):Graph(n1){} ~List (); }; //----------------------------------------------------
7.5. Главная функция
Главная функция носит демонстрационный характер:
int main() { textbackground(WHITE); textcolor(BLACK); clrscr(); char key; cout<< "key="; cin>>key; int num; cout<< "Number of Graph Tops:"; cin>> num;
//----граф задан списком---------------­ if (key=='L') { List g1 (num); // создание объекта класса List g1.input_graph(); // ввод графа g1.output_graph(); // вывод графа
7.6. Пример выполнения программы для иерархии классов
77
if(g1.adjacent(1,2)) // проверка смежности cout<<endl<<"Tops Adjacent" ; else cout<<endl<<"Tops NOT Adjacent"; } //---граф задан матрицей смежности------­ else if(key=='M') { Matr g2 (num); // создание объекта класса Matr g2.input_graph(); // ввод графа cout<<endl<<" Out Matrix"<<endl; g2.output_graph(); // вывод графа if(g2.adjacent(1,2)) // проверка смежности cout<<endl<<"Tops Adjacent" ; else cout<<endl<<"Tops NOT Adjacent"; } //----ошибка---------­ else cout<<"error"; getch(); return 0; } //----------------------------------------------------
В начале работы программы необходимо ввести значение ключа, ко­торый определит способ задания графа в списочной или матричной форме. Если значение ключа будет равно `L`, то граф задается в списочной форме и для этого будет создан объект класса List, а если значение ключа будет равно `M`, то граф задается в матричной форме и должен быть создан объ­ект класса Matr. Объекты классов создаются в статической памяти. Про­веряется смежность только вершин с номерами 1 и 2. Полный текст про­граммы приведен в прил. 6.
7.6. Пример выполнения программы для иерархии классов
Для графа (см. рис. 6), заданного в списочной форме, результат рабо­ты программы показан на рис. 22, а для этого же графа, заданного матрицей смежности, на рис. 23.
7. Иерархии классов для решения задач на графах
78
Рис. 23. Результат работы программы для графа в табличной форме
Рис. 22. Результат работы программы для графа в списочной форме
Вопросы для самоконтроля
79
Вопросы для самоконтроля
1. Может ли в С++ один и тот же класс быть производным от двух клас-
сов?
2. Каково назначение виртуального базового класса?
3. Можно ли создать объект абстрактного класса?
4. Можно ли создать объект класса, содержащего чистую виртуальную
функцию?
5. Если базовый класс является абстрактным, то при каком обязательном
условии можно создать объект производного класса?
6. С какой целью при построении иерархий классов используются чистые
виртуальные функции?
7. Измените главную функцию таким образом, что на смежность проверя-
лась любая пара вершин графа.
8. Измените иерархию классов (см. рис. 21) следующим образом:
class Graph { … }; class Matr : public Graph { … };
class List : public Graph { … }; class Task : public Matr { … }; class Task : public List { … };
Вспомните о множественном наследовании и исправьте ошибку в предыдущем тексте. В классе Task создайте функцию, которая вы­полняет те же действия, что и гавная функция.
80
8. ПРОГРАММИРОВАНИЕ ЗАДАЧ НА ГРАФАХ ПОВЫШЕННОЙ СЛОЖНОСТИ
8.1. Иерархия и назначение функций классов
Перечень сложных задач на графах приведен в прил. 1. Рассмотрим объектно-ориентированное программирование задачи определения паросо­четания симметрического графа. Ограничимся иерархией классов, показан­ной на рис. 24, т.е. будем рассматривать графы, заданные матрицей смеж­ности.
Класс Graph является, по-прежнему, абстрактным базовым клас- сом. Класс Matr является производным классом от абстрактного базового класса Graph, а класс Task является производным классом от класса Matr. В классе Task объявим функцию task1()для определения паро­сочетания графа. В классе Matr объявим функцию rand_grаph()для создания случайного графа, заданного матрицей смежности. В классе Task
Рис. 24. Иерархия классов для программирования задач на графах определения
class Graph { protected: int N; public: virtual void input_graph()=0; };
Graph
Task
Matr
class Matr : public Graph { protected: bool **matr; public: void input_graph(); };
class Task : public Matr { public: void task1(); void task2(); };
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]