Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Основы объектно-ориентированного программирования задач на графах. Учебное пособие
.pdf
3.9. Программа для работы с графом
31
void Graph::input_Graph()
{
for(int i=0; i<N; i++)
for(int j=0; j<N; j++)
{
cout << " Matr["<< i <<"]["<< j <<"]=";
cin >> Matr[i][j];
}
cout << endl;
}
//----------------------------------------------------
void Graph::out_Graph()
{
for(int i=0; i<N; i++)
for(int j=0; j<N; j++)
{
cout << " Matr["<< i <<"]["<< j <<"]=";
cout << Matr[i][j] <<endl;
}}
//----------------------------------------------------
void Graph::adjacency(int x11, int x22)
{
bool *mas = new bool[N];
for(int i=0; i<N; i++)
if(Matr[x11-1][i] || Matr[x22-1][i])
mas[i]=1;
else mas[i]=0;
mas[x11-1]=0; mas[x22-1]=0;
cout<<"\n Tops "<<x11<<" OR "<<x22<<" adjacency
tops : ";
for(int i=0; i<N; i++)
if(mas[i]) cout<< i+1<<” ”;
delete[] mas;
}
//----------------------------------------------------

3. Разработка класса для решения задач на графах
32
#pragma argsused
int main()
{
textbackground(WHITE);
textcolor(BLACK);
clrscr();
int number;
int x1,x2;
cin>>number;
Graph * graph = new Graph(number);
graph->input_Graph();
graph->out_Graph();
cout<<" Input number of two tops: ";
cin>>x1>>x2;
graph->adjacency(x1,x2);
delete graph;
getch();
return 0;
}
//------------------------------------------------
Выводы
Таким образом, разработан простой класс для решения задач на графах и рассмотрены, так называемые, инкапсуляции, под которыми понимается создание объектов классов как единого целого, состоящего из данных
и кода функций, а также использование меток классов для защиты данных
классов.
Описание класса содержит только три функции для работы с графами.
При необходимости можете добавить в описание класса новую функцию
для решения задачи на графах и организовать вызов этой функции так же,
как это сделано для функции adjacency(). Кроме того, можете поместить описание класса и его функций в отдельный заголовочный файл и
подключать его к своим программам, в которых требуется решение задач
на графах. В разработанных программах рассматривалось пока только ис-

Вопросы для самоконтроля
33
пользование классов. Однако объектно-ориентированное программирование начинается с использования таких возможностей С++, как наследование и полиморфизм.
Вопросы для самоконтроля
1. Какая из меток класса public или private определяет члены класса
как открытые для всех объектов этого класса программы, а какая как
закрытые?
2. Какой операции на графах, матрица смежности которого задана дву-
мерным массивом bool Matr[5][5], соответствует следующий
цикл, если x1 и x2 номера двух вершин этого графа:
for(int i=0; i<5; i++)
if(Matr[x1-1][i] || Matr[x2-1][i]) mas[i]=1;
else mas[i]=0;
3. Сколько объектов одного и того же класса можно создать с динамиче-
ской памяти, а сколько – в статической?
4. В чем особенность аргументов функций при перегрузке этих функций?
5. Сколько конструкторов и сколько деструкторов можно создать в одном
классе?
6. Обязательно или нет в классе создавать конструктор?
7. Может ли конструктор класса не иметь аргументов?
8. В каком случае в конструкторе класса можно использовать оператор
return?
9. Для чего может использоваться конструктор класса при программиро-
вании задач на графах?
10. Напишите код функции, которая задает ребра симметрического графа
случайным образом.
11. Чем по назначению отличаются главная и побочная диагонали матрицы
смежности симметрического графа?
12. Какой из объектов g1 или (*g2) класса Graph создается в динами-
ческой памяти:
Graph g1; Graph *g2; g2=new Graph;

3. Разработка класса для решения задач на графах
34
13. Какой из следующих записей функция f1() клаccа Graph определена
вне класса:
void Graph::f1(){} или void Graph->f1(){}
14. Чем отличается тип данных int от типа данных bool?
15. Какой операцией вызываются функции класса, объект которого cоздан
в динамической памяти: -> или >>?
16. В классе A задана переменная int b и функция f1() для ввода этой
переменной с клавиатуры:
class A
{ private: int b;
public: void f1() (cin>>b;)
};
main()
{
A a1;
a1.f1();
a1.b=7;
}
Будут ли сообщения компилятора об ошибке?
17. В чем отличие следующих двух проверок смежности двух вершин x1
и x2 графа G, заданного матрицей смежности, которая моделируется двумерным массивом Matr:
1) if(Matr[x1-1][x2-1]==1) cout<<1;
2) if(Matr[x2-1][x1-1]==1) cout<<1;
18. Какая задача на графах решена следующим кодом функции f1() клас-
са Graph, количество вершин которого равно 5:
void Graph::f1()
{
int s=0;
for(int i=0; i<5; i++)
s=s+Matr[i][i];
cout<<s;
}

35
4. ПРОСТОЕ НАСЛЕДОВАНИЕ ПРИ РАЗРАБОТКЕ КЛАССОВ
ДЛЯ РЕШЕНИЯ ЗАДАЧ НА ГРАФАХ
4.1. Базовый и производный класс
Наследование при программировании используется для разработки
иерархий классов, а также для внесения изменений в разработанные библиотеки классов.
Например, разработан класс Graph для решения задач на графах.
В классе описаны функции и данные, но требуется дополнить разработанный класс новыми операциями на графах. В этом случае, как уже отмечалось, можно просто внести изменения в сам класс Graph, а можно создать
класс производный от класса Graph и внести необходимые изменения
уже в производном классе. Класс Graph в этом случае называется базовым
классом. Производный класс наследует все функции и данные базового
класса, которые расположены в части public и protected. Члены класса из части private не наследуются. Если вы хотите, чтобы члены класса
из части private наследовались, то их необходимо поместить в часть pro-
tected, или оформить в виде свойств.
Наследовать – означает получить доступ фнукциям производного
класса к членам базового класса, т.е. при создании объекта производного
класса пользоваться членами базового класса, как-будто они являются членами самого производного класса.
Воспользуемся аппаратом наследования и внесем изменения в программу, разработанную в 3.7, так, чтобы можно было проверять, смежны
или нет любые две заданные вершины графа.
Для этого создадим класс My_Class, производный от класса
Graph:
class My_Class : public Graph
{
};
Заголовок производного класса оформляется просто. После имени
производного класса необходимо поставить двоеточие (:), написать имя
базового класса, а перед ним – public или private. Если написать

4. Простое наследование при разработке классов для решения задач на графах
36
public, то никаких проблем не возникнет. Членами базового класса из части public и protected можно пользоваться, как обычными членами
создаваемого класса. Если поставить private, то члены базового класса
из части public станут protected, а члены из части protected станут pri-
vate. Выбирайте сами, какой модификатор написать. Чтобы не создавать
себе проблем, мы выбрали public.
4.2. Функция для проверки смежности двух вершин графа
Назовите функцию для проверки смежности двух вершин графа ad-
jacent_nodes(). Вставьте ее прототип в описание производного класса:
class My_Class : public Graph
{
public: int adjacent_nodes(int, int);
};
Расположите текст функции вне описания класса:
int My_Class::adjacent_nodes(int x11, int x22)
{ if(Matr[x11-1][x22-1]) return 1;
else return 0;
}
Функция adjacent_nodes() по смыслу имеет два аргумента – но-
мера вершин, смежность которых необходимо установить. Функция должна возвращать 1, если вершины смежны, и возвращать 0, если вершины не
смежны.
Теперь внесите изменения в главную функцию, так как необходимо
создавать объект класса производного класса My_Class вместо объекта
базового класса Graph:
My_Class * graph = new My_Class(number);

4.4. Главная функция
37
4.3. Вызов конструктора базового класса
В базовом классе есть конструктор, который к тому же имеет аргумент. Поэтому обязательно нужно создать конструктор производного
класса. Этот конструктор в нашем примере должен обязательно иметь один
аргумент, значение которого он должен передать в конструктор базового
класса. Добавьте в описание производного класса конструктор:
class My_Class : public Graph
{
public: int adjacent_nodes(int, int);
My_Class(int a ):Graph(a){}
};
В нашем примере тело конструктора My_Class() не имеет операторов. Основная задача конструктора вызвать конструктор базового класса
и присвоить значение его аргументу. Когда будет создан объект класса
My_Class, будет вызван конструктор My_Class() и значение переменной number присвоено его фиктивному аргументу a. Затем будет вызван
конструктор Graph() базового класса. Так как в заголовке конструктора
My_Class() в списке переменных вызова конструктора Graph(а) напи-
сано имя переменной а, то именно ее значение будет присвоено аргументу
n1 конструктора Graph(). Затем конструктор Graph() присвоит данному своего класса переменной N значение числа вершин и создаст массив
Matr в динамической памяти. Только после этого будет выполнен конструктор My_Class() производного класса. В нашем примере никаких
действий не произойдет, так как конструктор пустой.
4.4. Главная функция
Добавьте в главную функцию операторы вызова функции
adjacent_nodes():
cout<< endl<<" Tops "<<x1<<" and "<<x2;
if(graph->adjacent_nodes(x1,x2))
cout<<" Adjacent";
else cout<< " No Adjacent";

4. Простое наследование при разработке классов для решения задач на графах
38
Перед тем, как запустить программу на выполнение, не забудьте в
классе Graph изменить метку private на protected.
4.5. Пример выполнения программы
Для графа (см. рис. 6) результат выполнения программы показан на
рис. 9. Полный текст программы приведен в прил. 2.
Рис. 9. Результат выполнения программы для графа, показанного на рис. 6
Вопросы для самоконтроля
1. В базовом классе некоторые члены класса размещены после метки
класса protected и производный класс определен с модификатором
доступа public, тогда в производном классе эти члены соответствуют
какому из атрибутов доступа: private, public или protected?
2. Может ли в базовом классе одновременно использоваться две метки
класса private и protected?
3. Можно ли создать производный класс My_Class следующей записью:
class My_Class : protected Graph {];
4. В каком случае конструктор производного класса имеет пустое тело?

Вопросы для самоконтроля
39
5. В каком случае конструктор производного класса должен иметь аргу-
менты, если тело этого конструктора не имеет операторов?
6. Может ли через конструктор производного класса вызвать два
конcтруктора базового класса?
7. Если конструктор базового класса не имеет аргументов, то обязательно
ли создавать в производном классе конструктор для вызова конструктора базового класса?
8. В каком порядке вызываются конструкторы базового и производного
классов?
9. В каком порядке вызываются деструкторы базового и производного
классов?
10. Нужно ли создавать в производном классе деструктор для вызова де-
структора базового класса?
11. Найдите ошибку в следующем коде исправьте ее и определите значение
переменной b, которое будет выведено на экран:
class A
{ public: int b;
A(int c) {b=c;}
};
class B : public A
{ public: B(int d) {b=d;}
void f1(){cout<<b;}
};
main()
{
B b1 (5); b1.f1();
}

40
5. ПОЛИМОРФИЗМ ПРИ РАЗРАБОТКЕ КЛАССОВ
ДЛЯ РЕШЕНИЯ ЗАДАЧ НА ГРАФАХ
5.1. Виртуальные функции
Полиморфизм – это одинаковое обозначение действий, реализуемых
по-разному. Полиморфизм в С++ реализован с помощью аппарата виртуальных функций.
Для того чтобы определить в классе виртуальную функцию, нужно в
начале ее заголовка написать ключевое слово virtual. Например, в классе Graph объявим функцию adjacency() как виртуальную функцию:
class Graph
{
private: int N;
bool **Matr;
public: Graph(int);
~Graph();
void input_Graph();
void out_Graph();
virtual void adjacency(int,int);
};
Для чего функция класса объявляется виртуальной? Когда разраба-
тывается библиотека классов, то разработчик понимает, что его созданием
кто-то будет пользоваться. Если какая-то функция объявлена им виртуальной, то это означает, что пользователь библиотеки может эту функцию переопределить, если ему не нравится, как эта функция реализована, или есть
необходимость разработать свою реализацию этой виртуальной функции.
Для этого необходимо создать свой производный класс от такого ба-
зового класса, в котором виртуальная функция была бы доступна. Затем в
производном классе необходимо переопределить виртуальную функцию.
Для этого заголовок переопределямой функции должен быть абсолютно
одинаковым с заголовком виртуальной функции. Одинаковыми должны
быть типы возвращаемых значений, имена, типы и количество аргументов.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
