Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Пример 10.7. Класс треугольников






 

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

Для реализации этой задачи составить описание класса треугольников на плос­кости. Предусмотреть возможность объявления в клиентской программе (main) экземпляра треугольника с заданными координатами вершин. Предусмотреть наличие в классе методов, обеспечивающих: 1) перемещение треугольников на плоскости; 2) определение отношения > для пары заданных треугольников (мера сравнения - площадь треугольников); 3) определение отношения включения типа: «Треугольник 1 входит в (не входит в) Треугольник 2».

Программа должна содержать меню, позволяющее осуществить проверку всех методов класса.

Применим гибридный подход: разработку главного клиента main() проведем по технологии функцио­нальной декомпозиции, а функции-серверы, вызываемые из main(), будут исполь­зовать объекты.

Начнем с выявления основных понятий/классов. Первый оче­видный класс Triangle необходим для представления треугольников (через три точки, задающие его вершины). Точку на плоско­сти представим с помощью пары вещественных чисел, задающих координаты точки по осям х и у.

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

Итак, объектно-ориентированная декомпозиция дала нам два класса: Triangle и Point.

Если класс В является «частным случаем» класса А, то говорят, что В is а А (напри мер, класс треугольников есть частный вид класса многоугольников: Triangle is a Polygon).

Если класс А содержит в себе объект класса В, то говорят, что A has а В (например, класс треугольников может содержать в себе объекты класса точек: Triangle has a Point).

Порядок перечисления вершин особо важен, так как в дальней­шем, решая подзадачу определения отношения включения одного треугольника в другой, мы будем рассматривать стороны треугольника как векторы. Условим­ся, что вершины треугольника перечисляются в направлении по часовой стрелке.

Займемся теперь основным клиентом - main(). Здесь мы применяем функцио­нальную декомпозицию, или технологию нисходящего проектирования. В соот­ветствии с данной технологией основной алгоритм представляется как последо­вательность нескольких подзадач. Каждой подзадаче соответствует вызываемая серверная функция. На начальном этапе проектирования тела этих функций могут быть заполнены «заглушками» - отладочной печатью. Если при этом в ка­кой-то серверной функции окажется слабое сцепление, то она в свою очередь разбивается на несколько подзадач.

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

На первом этапе мы напишем код для начального представления классов Point и Triangle, достаточный для того, чтобы создать несколько объектов типа Triangle и реализовать первый пункт меню - вывод всех объектов на экран.

Этап 1

///////////////////////////////////////////////////

// Проект Task1_2

/////////Point.h #ifndef POINT_H #define POINT_H

class Point

{

public:

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

Point(double _x = 0. double _y = 0): x(_x), y(_y) {}

// Другие методы

void Show() const; public:

double x, y:

};

#endif /* POINT_H */

///////////////////////////////////////////////////

// Point.cpp

#include < iostream>

#include " Point.h"

using namespace std;

void Point:: Show() const

{

cout «" (" «x «", " «у < < ")";

}

///////////////////////////////////////////////////

Triangle.h #ifndef TRIANGLE_H #define TRIANGLE_H

#include " Point.h"

class Triangle

{

public:

Triangle(Point, Point, Point, const char*); // конструктор

Triangle(const char*); // конструктор пустого (нулевого) треугольника

~Triangle(); // деструктор

Point Get_vl() const

{

return vl;

} // Получить значение vl

Point Get_v2() const

{

return v2;

} // Получить значение v2

Point Get_v3() const

{

return v3;

} // Получить значение v3

char* GetName() const

{

return name;

} // Получить имя объекта

void Show() const; // Показать объект

void ShowSideAndArea() const; // Показать стороны и площадь объекта

public:

static int count; // кол-во созданных объектов

private:

char* objID; // идентификатор объекта
char* name; // наименование треугольника
Point vl, v2, v3; // вершины
double a; // сторона, соединяющая vl и v2
double b; // сторона, соединяющая v2 и v3

double с; // сторона, соединяющая vl и v3

};

#endif /* TRIANGLE_H */

////////////////////////////////////////////////////

//Triangle.cpp

// Реализация класса Triangle

#include < math.h>

#include < iostream>

#inc1ude < iomanip>

#include < cstring>

//#include " CyrIOS.h". // for Visual C++ 6.0

#include " Triangle.h"

using namespace std;

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

Triangle:: Triangle(Point _v1. Point _v2, Point _v3, const char* ident)

: vl'(_vl), v2(_v2). v3(_v3)

{

char buf[16];

objID = new char[strlen(ident) + 1];

strcpy(objID. ident);

count++;

sprintf(buf. " Треугольник %d", count);

name = new char[strlen(buf) + 1];

strcpy(name, buf);

a = sqrt((vl.x - v2.x) * (vl.x - v2.x) + (vl.y - v2.y) * (vl.y - v2.y));

b = sqrt((v2.x - v3.x) * (v2.x - v3.x) + (v2.y - v3.y) * (v2.y - v3.y));

с = sqrt((vl.x - v3.x) * (vl.x - v3.x) + (vl.y - v3.y) * (vl.y - v3.y));

cout «" Constructor_1 for: " «objID «" (" «name «")" «endl; // отладочный вывод

}

// Конструктор пустого (нулевого) треугольника Triangle:: Triangle(const char* ident)

{

char buf[16];

objID = new char[strlen(ident) +1];

strcpy(objID. ident);

count++;

sprintf(buf, " Треугольник %d", count);

name = new char[strlen(buf) +1];

strcpy(name, buf);

a = b = с = 0;

cout «" Constructor_2 for: " «objID «" (" «name «")" «endl; // отладочный вывод

}

// Деструктор

Triangle:: ~Triangle()

{

cout «" Destructor for: " «objID «endl;

delete [] objID;

delete [] name;

}

// Показать объект

void Triangle:: Show() const

{

cout «name «": ";

vl.Show(): v2.Show(): v3.Show();

cout «endl;

}

// Показать стороны и площадь объекта

void Triangle:: ShowSideAndArea() const

{

double p = (a + b + c) / 2;

double s = sqrt(p * (p - a) * (p - b) * (p - c);

cout «" -- " «endl;

cout «name «": ";

cout.precision(4);

cout «" a= " «setw(5) «a;

cout «", b= " «setw(5) «b;

cout «", c= " «setw(5) «c;

cout «": \ts= " «s «endl;

}

////////////////////////////////////////////////

// Main.cpp

#include < iostream>

#include " Triangle.h"

//#include " CyrIOS.h" // for Visual C++ 6.0

using namespace std;

int Menu();

int GetNumber(int, int);

void ExitBack();

void Show(Triangle* [], int);

void Move(Triangle* [], int);

void FindMax(Triangle* [], int);

void IsIncluded(Triangle* [], int);

// Инициализация глобальных переменных

int Triangle:: count =0;

// ---- главная функция

int main()

{

// Определения точек

Point pl(0, 0); Point p2(0.5, 1);

Point p3(l, 0); Point p4(0, 4.5);

Point p5(2. 1); Point p6(2. 0);

Point p7(2, 2); Point p8(3, 0);

// Определения треугольников

Triangle triaA(pl, p2, рЗ, " triaA");

Triangle triaB(pl. p4, p8, " triaB");

Triangle triaC(pl. p5. p6. " triaC");

Triangle triaD(pl. p7. p8. " triaD");

// Определение массива указателей на треугольники

Triangle* pTria[] = { & triaA, & triaB. & triaC, & triaD }; int n = sizeof (pTria) / sizeof (pTria[0]);

// Главный цикл

bool done = false: whfie (! done)

{

switch (Menu())

{

case 1: Show(pTria, n); break:

case 2: Move(pTria, n); break;

case 3: FindMax(pTria, n); break;

case 4: IsIncluded(pTria, n); break;

case 5: cout «" Конец работы." «endl;

done = true; break;

}

}

return 0;

}

II ----- вывод меню

int Menu()

{

cout «" \n===== Главное меню =====" «endl;

cout «" 1 - вывести все объекты\t 3 - найти максимальный" «endl;

cout «" 2 - переместить\t\t 4 - определить отношение включения" «endl;

cout «" \t\t 5 - выход" «endl;

return GetNumber(1, 5);

}

// ввод целого числа в заданном диапазоне

int GetNumber(int min, int max)

{

int number = min - 1; while (trye)

{

cin» number;

if ((number > = min) & & (number < = max) & & (cin.peek() == '\n'));

break;

else

{

cout «" Повторите ввод (ожидается число от " «min «" до " «max «"): " «endl; cin.clear();

while (cin.get()! = '\n') {};

}

}

return number;

}

// возврат в функцию с основным меню

void ExitBack()

{

cout «" Нажмите Enter." «endl;

cin.get(); cin.get();

}

// - вывод всех треугольников

void Show(Triangle* p_tria[]. int k)

{

cout «" === Перечень треугольников ===" «endl;

for (int i = 0; i < k: ++i) p_tria[i]-> Show();

for (i = 0; i < k; ++i)

p_tria[i]-> ShowSideAndArea();

ExitBack();

II- перемещение

void Move(Triangle* p_tria[], int k)

{

cout «" ======= Перемещение ======" «endl;

// здесь будет код функции...

ExitBack();

}

// поиск максимального треугольника

void FindMax(Triangle* p_tria[], int k)

{

cout «" = Поиск максимального треугольника =" «endl;

// здесь будет код функции...

ExitBack();

}

// определение отношения включения

void Islncluded(Triangle* p_tria[], int k)

{

cout «" ===== Отношение включения =====" «endl;

// здесь будет код функции...

ExitBack();

}

//---------------- конец проекта Task1_2

//////////////////////////////////////////////

Рекомендуем вам обратить внимание на следующие моменты в проекте Task1_2.

1. Класс Point (файлы Point.h, Point.cpp).

Реализация класса Point пока что содержит единственный метод Show(), назначение которого очевидно: показать объект типа Point на экране. Здесь следует заметить, что при решении реальных задач в какой-либо графиче­ской оболочке метод Show() действительно нарисовал бы нашу точку, да еще в цвете. Но мы-то изучаем «чистый» C++, так что придется удо­вольствоваться текстовым выводом на экран основных атрибутов точки - ее координат.

2. Класс Triangle (файлы Triangle.h, Triangle.cpp).

Назначение большинства полей и методов очевидно из их имен и коммен­тариев.

Поле static int count играет роль глобального счетчика создаваемых объ ектов; мы сочли удобным в конструкторах генерировать имена треугольни ков автоматически: «Треугольник 1», «Треугольник 2» и т. д., используя текущее значение count (возможны и другие способы именования тре угольников).

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

Метод ShowSideAndArea() введен также только для целей отладки, - убе­дившись, что стороны треугольника и его площадь вычисляются правиль­но (с помощью калькулятора), в дальнейшем этот метод можно удалить.

Конструктор пустого (нулевого) треугольника предусмотрен для создания временных объектов, которые могут модифицироваться с помощью при­сваивания.

Метод Show() - см. комментарий выше по поводу метода Show() в классе Point. К сожалению, здесь нам тоже не удастся нарисовать треугольник на экране; вместо этого печатаются координаты его вершин.

3. Основной модуль (файл Main.cpp).

Инициализация глобальных переменных: обратите внимание на опера­тор int Triangle:: count = 0: - если вы забудете это написать, компилятор очень сильно обидится.

Функция main ():

- определения восьми точек p1,..., р8 выбраны произвольно, но так, чтобы из них можно было составить треугольники;

- определения четырех треугольников сделаны тоже произвольно, впослед­ствии на них будут демонстрироваться основные методы класса; однако не забывайте, что вершины в каждом треугольнике должны перечисляться по часовой стрелке;

- далее определяются массив указателей Triangle* pTria[] с адресами объяв­ленных выше треугольников и его размер n; в таком виде удобно переда­вать адрес pTria и величину n в вызываемые серверные функции;

- главный цикл функции довольно прозрачен и дополнительных поясне­ний не требует.

Функция Menu () - после вывода на экран списка пунктов меню вызывается функция GetNumber(), возвращающая номер пункта, введенный пользователем с клавиатуры.

Функция Show() пpocто выводит на экран перечень всех треугольников. В завершение вызывается функция ExitBack(), которая обеспечивает заключительный диалог с пользователем после обработки очередного пунк­та меню.

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

Тестирование и отладка первой версии программы

После компиляции и запуска программы вы должны увидеть на экране следую­щий текст:

Constructor_1 for: triaA (Треугольник 1)

Constructor_1 for: tnaB (Треугольник 2)

Constructor_1 for: triaC (Треугольник 3)

Constructor_1 for: triaD (Треугольник 4)

=============== Главное меню===============

1 - вывести все объекты 3 - найти максимальный

2 - переместить 4 - определить отношение включения

5 - выход

Введите с клавиатуры цифру 12. Программа выведет:

1
======= Перечень треугольников ========

Треугольник 1: (0. 0) (0.5. 1) (1, 0)

Треугольник 2: (0, 0) (0. 4.5) (3. 0)

Треугольник 3: (0. 0) (2. 1) (2. 0)

Треугольник 4: (0. 0) (2. 2) (3, 0)

Треугольник 1: а=1.118. b=1.118, с= 1: s=0.5

Треугольник 2: а=4.5, b=5.408, с=3: s= 6.75

Треугольник 3: а=2.236, b=1, с=2: s= 1

Треугольник 4: а=2.828, b=2.236, с=3: s=3

Нажмите Enter.

После ввода числовой информации всегда подразумевается нажатие клавиши Enter. Выбор первого пункта меню проверен. Нажмите Enter. Программа выведет:

=========== Главное меню ===========

Теперь проверим выбор второго пункта меню. Введите с клавиатуры цифру 2. На экране должно появиться:

================== Перемещение ===============

Нажмите Enter.

Выбор второго пункта проверен. Нажмите Enter. Программа выведет:

============ Главное меню =============

Теперь проверим ввод ошибочного символа. Введите с клавиатуры любой бук­венный символ, например w, и нажмите Enter. Программа должна выругаться:

Повторите ввод (ожидается число от 1 до 5):

Проверяем завершение работы. Введите цифру 5. Программа выведет:

Конец работы.

Destructor for: triaD

Destructor for: triaC

Destructor for: triaB

Destructor for: triaA

Тестирование закончено. Обратите внимание на то, что деструкторы объектов вызываются в порядке, обратном вызову конструкторов.

Продолжим разработку программы. На втором этапе мы добавим в классы Point и Triangle методы, обеспечивающие перемещение треугольников, а в основной модуль - реализацию функции Move(). Кроме этого, в классе Triangle мы уда­лим метод ShowSideAndArea(), поскольку он был введен только для целей отладки и свою роль уже выполнил.

Этап 2

Внесите следующие изменения в тексты модулей проекта.

1. Модуль Point.h: добавьте сразу после объявления метода Show() объявление операции-функции «+=», которая позволит нам впоследствии реализовать ме­тод перемещения Move() в классе Triangle:

void operator +=(Point&);

2. Модуль Point.cpp. Добавьте код реализации данной функции:

void Point:: operator +=(Point& p)

{

x += p.x: у += р.у:

}

3. Модуль Triangle.h.

Удалите объявление метода ShowSideAndArea().

Добавьте объявление метода:

void Move(Point);

4. Модуль Triangle.cpp. Удалите метод ShowSideAndArea().

Добавьте код метода Move():

// Переместить объект на величину (dp.x. dp.у) void Triangle:: Move(Point dp)

{

vl += dp;

v2 += dp;

v3 += dp;

}

5. Модуль Main.cpp.

В список прототипов функций в начале файла добавьте сигнатуру:

double GetDouble();

Добавьте в файл текст новой функции GetDouble() либо сразу после функ­ции Show(), либо в конец файла. Эта функция предназначена для ввода ве­щественного числа и вызывается из функции Move(). В ней предусмотрена защита от ввода недопустимых (например, буквенных) символов анало­гично тому, как это решено в функции GetNumber():

// ---- ввод вещественного числа

double GetDouble()

{

double value; while (true)

{

cin» value;

if (cin.peek() == '\n') break;

else

{

cout «" Повторите ввод (ожидается вещественное число): " «endl; cin.clear();

while (cin.get()! = '\n') {};

}

}

return value;

}

Замените заглушку функции Move() следующим кодом:

// -------- перемещение

void Move(Triangle* p_tria[]. int k)

{

cout «" ========= Перемещение =========" «endl;

cout «" Введите номер треугольника (от 1 до " «к «"): ";

int i = GetNumber(d, k) - 1;

p_tria[i]-> Show();

Point dp;

cout «" Введите смещение по х: ";

dp.x = GetDouble(): cout «" Введите смещение по у: ";

dp.у = GetDouble();

p_tria[i]-> Move(dp);

cout «" Новое положение треугольника: " «endl;

p_tria[i]-> Show();

ExitBack();

}

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

============= Перемещение =============

Введите номер треугольника (от 1 до 4): 1

Треугольник 1: (0. 0) (0.5. 1) (1, 0)

Введите смещение по х: 2.5

Введите смещение по у: -7

Новое положение треугольника:

Треугольник 1: (2.5, -7) (3, -6) (3.5, -7)

Нажмите Enter.

Продолжим разработку программы.

Этап 3

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

1. Модуль Triangle. h: добавьте объявление функции-операции:

bool operator > (const Triangle&) const;

2. Модуль Triangle.cpp: добавьте код реализации функции-операции:

// Сравнить объект (по площади) с объектом tria

bool Triangle:: operator > (const Triangles tria) const

{

double p = (a + b + c) / 2;

double s = sqrt(p * (p - a) * (p - b) * (p - c));

double p1 = (tria a + tria b + tria c) / 2;

double s1 = sqrt(pl * (p1 - tria.a) * (p1 - tria.b) * (pi - tria.c));

if (s > s1) return true;

else

return false;

}

 

3. Модуль Main.cpp: замените заглушку функции FindMax() следующим кодом:

// -- поиск максимального треугольника

void FindMaxCTriangle* p_tria[]. int k)

{

cout «" = Поиск максимального треугольника =" «endl;

// Создаем объект triaMax, который по завершении поиска будет

// идентичен максимальному объекту.

// Инициализируем его значением 1-го объекта из массива объектов.

Triangle triaMax(" triaMax"):

triaMax = *p_tria[0];

// Поиск

for (int i = 1: " i < 4: ++i) if (*p_tria[i] > triaMax) triaMax = *p_tria[i];

cout «" Максимальный треугольник: " «triaMax.GetName() «endl;

ExitBack();

}

Откомпилируйте программу и запустите. Выберите третий пункт меню. На экра­не должно появиться:

=== Поиск максимального треугольника ==

Constructor_2 for: triaMax (Треугольник 5)

Максимальный треугольник: Треугольник 2

Нажмите Enter.

Как видите, максимальный треугольник найден правильно. Нажмите Enter. Появится текст:

Destructor for: triaB

============ Главное меню==============

1 - вывести все объекты 3 - найти максимальный

2 - переместить 4 - определить отношение включения

5 - выход

Чтобы завершить работу, введите цифру 5 и нажмите Enter. На передний план выскочит диалоговая панель с жир­ным белым крестом на красном кружочке и с сообщением об ошибке:

Debug Assertion Failed!

Program: C: \.... MAIN.EXE File: dbgdel.cpp Line 47

Программа выведет на экран следующее:

Конец работы.

Destructor for: triaD

Destructor for: triaC

Destructor for: ННННННННННээээЭЭЭЭЭЭЭЭЭЭО

В предыдущем выводе нашей программы после того как функция FindMax() выполнила основную работу и вывела на экран сооб­щение

Максимальный треугольник: Треугольник 2

программа пригласила пользователя нажать клавишу Enter. Это приглашение выводится функцией ExitBack(). А вот после нажатия клавиши Enter на экране появился текст:

Destructor for: triaB

после которого опять было выведено главное меню.

Значит, деструктор для объекта triaB был вызван в момент возврата из функ­ции FindMax(). Внутри функции объявлен объект triaMax, и мы даже видели работу его конст­руктора:

Constructor_2 for: triaMax (Треугольник 5)

А где же вызов деструктора, который по идее должен произойти в момент воз­врата из функции FindMax()? Объект triaMax после своего объявления неоднократно модифицируется с помощью операции присваивания. Последняя такая модифи­кация происходит в цикле, причем объекту triaMax присваивается значение объ- екта triaB. А теперь давайте вспомним, что если мы не перегрузили операцию присваивания для некоторого класса, то компилятор сделает это за нас, но в та­кой «операции присваивания по умолчанию» будут поэлементно копироваться все поля объекта. При наличии же полей типа указателей возможны проблемы, что мы и получили.

В поля objID и name объекта triaMax были скопированы значения одноименных полей объекта triaB. В момент выхода из функции FindMax() деструктор объекта освободил память, на которую указывали эти поля. А при выходе из основной функции main() деструктор объекта triaB попытался еще раз освободить эту же память. Это делать нельзя, потому что этого делать нельзя никогда.

Нужно добавить в класс Triangle пере­грузку операции присваивания, а заодно и конструктор копирования.

Внесите следующие изменения в тексты модулей проекта:

1. Модуль Triangle.h.

Добавьте объявление конструктора копирования:

Triangle(const Triangle&); // конструктор копирования

Добавьте объявление операции присваивания:

Triangle& operator =(const Triangle&);

2. Модуль Triangle. cpp.

Добавьте реализацию конструктора копирования:

// Конструктор копирования

Triangle:: Triangle(const Triangle& tria): vl(tria.vl), v2(tria.v2),

v3(tria.v3)

{

cout «" Copy constructor for: " «tria.objID «endl; // отладочный вывод

objID = new char[strlen(tria.objID) + strlen(копия)") + 1];

strcpy(objID, tria.objID): strcat(objID, " (копия)");

name = new char[strlen(tria.name) +1]: strcpy(name. tria.name); a = tria.a; b = tria.b; с = tria.с;

}

Добавьте реализацию операции присваивания:

// Присвоить значение объекта tria

Triangle& Triangle:: operator =(const Triangle& tria)

{

cout «" Assign operator: " «objID «" = " «tria.objID «endl; // отладочный вывод

if (& tria == this) return *this; delete [] name;

name = new char[strlen(tria.name) + 1]; strcpy(name, tria.name);

a = tria.a; b = tria.b; с = tria.с;

return *this;

}

И в конструкторе копирования, и в операторе присваивания перед копировани­ем содержимого полей, на которые указывают поля типа char*, для них выделяется новая память. Обратите внимание, что в конструкторе копирования после переписи поля objID мы добавляем в конец этой строки текст «(копия)». А в опера­ции присваивания это поле, идентифицирующее объект, вообще не затрагивается и остается в своем первоначальном значении. Все это полезно для целей отладки.

Откомпилируйте и запустите программу. Выберите третий пункт меню. На экра­не должен появиться текст:

=== Поиск максимального треугольника ==

Constructor_2 for: triaMax (Треугольник 5)

Assign operator: triaMax = triaA

Assign operator: triaMax = triaB

Максимальный треугольник: Треугольник 2

Нажмите Enter.

Обратите внимание на отладочный вывод операции присваивания. Продолжим тестирование. Нажмите Enter. Программа выведет:

Destructor for: triaMax

============== Главное меню===============

1 - вывести все объекты 3 - найти максимальный

2 - переместить4 - определить отношение включения

5 - выход

Обратите внимание на то, что был вызван деструктор для объекта triaMax, а не triaB. Продолжим тестирование. Введите цифру 5. Программа выведет:

Конец работы.

Destructor for: triaD

Destructor for: triaC

Destructor for: triaB

Destructor for: triaA

Осталось теперь решить самую сложную подзада­чу - определение отношения включения одного треугольника в другой.

Этап 4

Из многочисленных подходов к решению этой подзадачи наш выбор остановил­ся на алгоритме, в основе которого лежит определение относительного положе­ния точки и вектора на плоскости1. Вектор - это направленный отрезок прямой линии, начинающийся в точке beg_p и заканчивающийся в точке end_p. При графическом изображении конец вектора украшают стрелкой. Теперь призовите ваше пространственное воображение или вооружитесь карандашом и бумагой, чтобы проверить следующее утверждение. Вектор (beg_p, end_p) делит плоскость на пять непересекающихся областей:

1) все точки слева от воображаемой бесконечной прямой1, на которой лежит
наш вектор (область LEFT),

2) все точки справа от воображаемой бесконечной прямой, на которой лежит наш вектор (область RIGHT),

3) все точки на воображаемом продолжении прямой назад от точки beg_p в бес­конечность (область BEHIND),

4) все точки на воображаемом продолжении прямой вперед от точки end_p в бесконечность (область AHEAD),

5) все точки, принадлежащие самому вектору (область BETWEEN).

Для выяснения относительного положения точки, заданной некоторым объек­том класса Point, добавим в класс Point перечисляемый тип:

enum ORIENT { LEFT. RIGHT. AHEAD. BEHIND, BETWEEN }:

а также метод C1assify(beg_p, end_p), возвращающий значение типа ORIENT для данной точки относительно вектора (beg_p, end_p).

Обладая этим мощным методом, совсем нетрудно определить, находится ли точ­ка внутри некоторого треугольника. Мы договорились перед началом решения задачи, что треугольники будут задаваться перечислением их вершин в порядке изображения их на плоскости по часовой стрелке. То есть каждая пара вершин образует вектор, и эти векторы следуют один за другим по часовой стрелке. При этом условии некоторая точка находится внутри треугольника тогда и только тогда, когда ее ориентация относительно каждой вектора-стороны треугольника имеет одно из двух значений: либо RIGHT, либо BETWEEN. Эту подзадачу будет ре­шать метод InTriangle() в классе Point.

Изложим по порядку, какие изменения нужно внести в тексты модулей. 1. Модуль Point.h.

Добавьте перед объявлением класса Point объявление нового типа ORIENT, а также упреждающее объявление типа Triangle, чтобы имя типа Triangle было известно компиля­тору в данной единице трансляции, так как оно используется в сигнатуре метода InTriangle().:

enum ORIENT

{

LEFT. RIGHT. AHEAD, BEHIND, BETWEEN

};

class Triangle;

Добавьте внутри класса Point объявления функций:

Point operator +(Point&);

Point operator -(Point&);

double Length() const; // определяет длину вектора точки в полярной системе координат

ORIENT Classify(Point&, Point&) const; // определяет положение точки относительно вектора, заданного двумя точками
bool InTriangle(Triangle&) const; // определяет, находится ли точка внутри треугольника

 

Функция-операция «-» и метод Length() будут использованы при реализации метода Classify(), а функция-операция «+» добавлена для симметрии. Метод Classify(), в свою очередь, вызывается из метода InTriangle(). 1. Модуль Point.cpp.

Добавьте после директивы #include < iostream> директиву #include < math.h>.

Она необходима для использования функции sqrt(x) из математической библиотеки C++ в алгоритме метода Length().

Добавьте после директивы #include «Point. h» директиву #include «Triangle. h».

Последняя необходима в связи с использованием имени класса Triangle в данной единице трансляции.

Добавьте реализацию функций-операций:

Point Point:: operator +(Point& p)

{

return Point(x + p.x, у + р.у);

}

Point Point:: operator -(Points p)

{

return Point(x - p.x, у - p.у);

}

Добавьте реализацию метода Length():

double Point:: Length() const

{

return sqrt(x*x + y*y);

}

Добавьте реализацию метода Classify():

ORIENT Point:: Classify(Point& beg_p. Points end_p) const

{

Point p0 = *this; Point a = end_p - beg_p; Point b = p0 - beg_p; double sa = a.x * b.y - b.x * a.y;

if (sa > 0.0) return LEFT;

if (sa < 0.0) return RIGHT;

if ((a.x * b.x < 0.0) || (a.y * b.y < 0.0)) return BEHIND;

if (a. Length О < b. Length()) return AHEAD;

return BETWEEN;

}

Аргументы передаются в функцию по ссылке - это позволяет избежать вызова конструктора копирования.

Добавьте реализацию метода InTriangle():

bool Point:: InTriangle(Triangle& tria) const

{

ORIENT orl = Classify(tria.Get_vl(), tria.Get_v2O); ORIENT or2 = Classify(tria.Get_v2(), tria.Get_v3O): ORIENT огЗ = Classify(tria.Get_v3(), tria.Get_vl());

if ((orl == RIGHT || orl == BETWEEN)

& & (or2 == RIGHT || or2 == BETWEEN)

& & (оrЗ == RIGHT || оrЗ == BETWEEN)) return true;

else return false;

}

2. Модуль Triangle.h: добавьте в классе Triangle объявление дружественной функции (все равно, в каком месте):

friend bool TriaInTna(Triangle, Triangle); // Определить, входит ли один треугольник во второй

3. Модуль Triangle.cpp: добавьте в конец файла реализацию внешней дружест­венной функции:

// Определить, входит ли треугольник trial в треугольник tria2

bool TriaInTria(Triangle trial, Triangle tria2)

{

Point v1 = trial.Get_v1();

Point v2 = trial.Get_v2();

Point v3 = trial.Get_v3();

return (vl.InTriangle(tria2) & & v2.InTriangle(tria2) & & v3.InTriangle(tria2));

return true;

}

Результат, возвращаемый функцией, основан на проверке вхождения каждой вершины первого треугольника (trial) во второй треугольник (tria2).

4. Модуль Main.cpp: замените заглушку функции IsIncluded() следующим кодом:

// - определение отношения включения

void IsIncluded(Triangle* p_tria[]. int k)

{

cout «" ==== Отношение включения ====" «endl;

cout «" Введите номер 1-го треугольника (от 1 до " «к «"): ";

int i1 = GetNumber(1. к) - 1;

cout «" Введите номер 2-го треугольника (от 1 до " «к «"): ";

int i2 = GetNumber(1. к) - 1;

if (TriaInTria(*p_tria[il], *p_tria[i2]))

cout «p_tria[i1]-> GetName() «" - входит в - " «p_tria[i2]-> GetName() «endl;

else

cout «p_tria[i1]-> GetName() «" - не входит в - " «p_tria[i2]-> GetName() «endl; ExitBack();

}

Модификация проекта завершена. Откомпилируйте и запустите программу. Вы­берите четвертый пункт меню. Следуя указаниям программы, введите номера сравниваемых треугольников, например 1 и 2. Вы должны получить следующий результат:

======== Отношение включения ==========

Введите номер 1-го треугольника (от 1 до 4): 1 Введите номер 2-го треугольника (от 1 до 4): 2 Copy constructor for: triaB Copy constructor for: triaA Destructor for: triaA(копия) Destructor for: triaB(копия) Треугольник 1 - входит в - Треугольник 2 Нажмите Enter.

 

Аппаратура и материалы. Для выполнения лабораторной работы необходим персональный компьютер со следующими характеристиками: процессор Intel Pentium-совместимый с тактовой частотой 800 МГц и выше, оперативная память - не менее 64 Мбайт, свободное дисковое пространство - не менее 500 Мбайт, устройство для чтения компакт-дисков, монитор типа Super VGA (число цветов от 256) с диагональю не менее 15². Программное обеспечение - операционная система Windows2000/XP и выше, среда разработки приложений Microsoft Visual Studio.

Указания по технике безопасности. Техника безопасности при выполнении лабораторной работы совпадает с общепринятой для пользователей персональных компьютеров, самостоятельно не производить ремонт персонального компьютера, установку и удаление программного обеспечения; в случае неисправности персонального компьютера сообщить об этом обслуживающему персоналу лаборатории (оператору, администратору); соблюдать правила техники безопасности при работе с электрооборудованием; не касаться электрических розеток металлическими предметами; рабочее место пользователя персонального компьютера должно содержаться в чистоте; не разрешается возле персонального компьютера принимать пищу, напитки.

Методика и порядок выполнения работы. Перед выполнением лабораторной работы каждый студент получает индивидуальное задание. Защита лабораторной работы происходит только после его выполнения (индивидуального задания). При защите лабораторной работы студент отвечает на контрольные вопросы, приведенные в конце, и поясняет выполненное индивидуальное задание. Ход защиты лабораторной работы контролируется преподавателем.Порядок выполнения работы:

1. Проработать примеры, приведенные в лабораторной работе.

2. Реализовать класс Time.

3. Реализовать класс треугольников.

4. Решить задачу на двумерные массивы согласно своему варианту лабораторной работы №6 с использованием классов.

5. Реализовать класс многочленов и смоделировать операции над ними, принимая во внимание то, что многочлен – это объект, имеющий свойства: коэффициенты и степени.

Содержание отчета и его форма. Отчет по лабораторной работе должен состоять из:

1. Названия лабораторной работы.

2. Цели и содержания лабораторной работы.

3. Ответов на контрольные вопросы лабораторной работы.

4. Формулировки заданий и порядка их выполнения.

Отчет о выполнении лабораторной работы в письменном виде сдается преподавателю.

Вопросы для защиты работы

1. Какова структура программы на объектно-ориентированном языке?

2. Что такое объект ООП?

3. Что такое класс?

4. На какие разделы может делиться класс?

5. Назовите и охарактеризуйте три основных принципа в использовании классов.

6. Что такое конструктор и для чего он используется?

7. Как происходит индексирование объектов класса?

8. Как происходит вызов функции?

9. Что такое композиция классов?

10. Для чего и каким образом используются дружественные функции?

11. Для чего требуется указатель this?

12. Для чего используется модификатор static?

 

 

 

Список рекомендуемой литературы

 

а) Основная литература:

1. Подбельский В.В. Язык Си++: Учеб. пособие. 5-е изд. - М.: Финансы и статистика, 2001.

2. Павловская Т.А. C/C++. Программирование на языке высокого уровня - СПб.: Питер, 2005.

3. C/C++. Структурное программирование: Практикум / Т.А. Павловская, Ю.А. Щупак - СПб.: Питер, 2005.

4. Павловская Т.А., Щупак Ю.А. C++. Объектно-ориентированное программирование: Практикум. - СПб.: Питер, 2005.

5. Культин Н. C++ Builder. - СПб.: БХВ-Петербург, 2004.

6. Шамис В. Borland C++ Builder 6. - СПб.: Изд-во Питер, 2004.

7. Послед Б.С. C++ Borland Builder 6. - М.: Диасофт, 2003.

8. Шилдт Г. Самоучитель С++: Пер. с англ. 3-е изд. - СПб.: БХВ-Петербург, 2001.

 

б) Дополнительная литература:

1. Страуструп Б. Язык программирования Си++: Пер. с англ. - М.: Радио и связь, 1991.

2. Эллис М., Страуструп Б. Справочное руководство по языку программирования С++ с комментариями. Проект стандарта ANSI: Пер. с англ. - М.: Мир, 1992.

3. Буч Г. Объектно-ориентированное проектирование с примерами применения: Пер. с англ. - М.: Конкорд, 1992.

4. Буч Г. Объектно-ориентированный анализ и проектирование с примерами приложений на C++, 2-е изд. / Пер. с англ. - М.: «Изд-во Бином», 1999.

5. Сурков К.А. Сурков Д.А. Вальвачев А.Н. Программирование в среде C++ Builder. - Мн.: ООО «Попурри», 1998.

 






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.