Розовая методичка. Огнева М. В., Кудрина Е. В. Структуры данных и алгоритмы программирование на языке c
Скачать 1.93 Mb.
|
3.6. Пример простого класса Создать класс Point, который содержит следующие члены класса 1. Поля: • int х, у ; 2. Конструкторы, позволяющие создать экземпляр класса: • с нулевыми координатами; • с заданными координатами. 3. Функции, позволяющие: • определить общее количество точек • установить координаты точки; 20 • вывести координаты точки на экран; • рассчитать расстояние от начала координат до точки. Кроме того, необходимо реализовать перегрузку: • операции, которая одновременно уменьшает значение полей х и у на 1; • операции бинарный +, которая позволяет сложить координаты двух точек, получив новую точку (вариант 1); • операции бинарный +, которая позволяет увеличить координаты точки на заданное значение (вариант 2). Реализация и использование класса: #include #include #include using namespace std; class Point { private : //координаты точки int x; int y; static int count; //количество объектов public : //конструктор с нулевыми координатами Point(): x(0), y(0) { count++; } //конструктор с заданными координатами Point( int х, int у): х(х), у(у) { count++; } //функция позволяет определить количество объектов типа Point int GetCount() { return count; } //функция позволяет установить новые координаты точки void SetXY( int х, int у) { this ->x = x; this ->y = y; } //функция позволяет вывести на экран информацию о точке void Show( string info) { cout << "Point " << info << ": (" << x << ", " << y << ")" << endl; cout << "Distance: " << GetDistance() << endl << endl; } 21 //функция позволяет вычислить расстояние от начала координат до точки double GetDistance() { return sqrt((double)(x*x + y*y)); } //перегрузка операции ++ Point operator ++() { return Point (++x, ++y); } //перегрузка операции -- Point operator --() { return Point (--x, --y); } //вариант 1 перегрузки операции + Point operator +( Point temp) { return Point (x+temp.x, y+temp.y); } //вариант 2 перегрузки операции + Point operator +( int a) { return Point (x+a, y+a); } }; int Point ::count = 0; //инициализация статического поля int main() { Point pointA; //создаем точку А с нулевыми координатами pointA.Show( "A" ); pointA.SetXY(-3,4); //устанавливаем новые координаты точки А pointA.Show( "A" ); Point pointB(3,4); //создаем точку В с заданными координатами pointB.Show( "B" ); --pointB; //демонстрация перегрузки операции -- pointB.Show( "B" ); //демонстрация варианта 1 перегрузки операции + Point pointC = pointA + pointB; pointC.Show( "C" ); 22 //демонстрация варианта 2 перегрузки операции + Point pointD = pointC + 3; pointD.Show( "D" ); //выводим общее количество точек cout << "Count of point: " << pointD.GetCount(); return 0; } Результат работы программы: Point A: (0, 0) Distance: 0 Point A: (-3, 4) Distance: 5 Point B: (3, 4) Distance: 5 Point B: (2, 3) Distance: 3.60555 Point C: (-1, 7) Distance: 7.07107 Point D: (2, 10) Distance: 10.198 Count of Point: 5 Задание. Как мы видим из примера, у нас существует четыре точки А, В, С, D Подумайте и объясните, почему функция GetCount() выдала нам в качестве общего количества существующих объектов класса Point значение 5. Исправьте данную ошибку. 3.7. Практикум №1 Замечание. Все классы должны содержать конструкторы (без параметров и конструктор инициализации), а также статическое поле для подсчета количества экземпляров класса. Работу классов продемонстрируйте на примерах. Задание 1. Создать класс Тriangle, содержащий следующие члены класса: 1. Поля: • int а, b, с; 2. Функции, позволяющие: • вывести на экран информацию о треугольнике; • рассчитать периметр треугольника; • рассчитать площадь треугольника. • установить длины сторон треугольника; • установить, существует ли треугольник с данными длинами сторон . Кроме того, необходимо реализовать перегрузку: 23 • операции ++ одновременно увеличивает (уменьшает) значение полей а, b и с на 1; • операции *: умножает поля а, b и с на заданный скаляр. Задание 2. Создать класс Rectangle, содержащий следующие члены класса: 1. Поля: • int а, b; 2. Функции, позволяющие: • вывести на экран информацию о прямоугольнике; • рассчитать периметр прямоугольника; • рассчитать площадь прямоугольника. • установить длины сторон прямоугольника; • установить, является ли данный прямоугольник квадратом. Кроме того, необходимо реализовать перегрузку: • операции ++ (--): одновременно увеличивает (уменьшает) значение полей а и b; • операции *: умножает поля а и b на заданный скаляр. Задание 3. Создать класс Money, содержащий следующие члены класса: 1. Поля: • int nominal; //номинал купюры • int amount; //количество купюр 2. Функции, позволяющие: • вывести номинал и количество купюр на экран; • определить, хватит ли денежных средств на покупку товара на сумму X рублей; • определить, сколько штук товара стоимости Y рублей можно купить на имеющиеся денежные средства; • установить значение полей; • рассчитать сумму денег. Кроме того, необходимо реализовать перегрузку: операции ++ (--): увеличивает (уменьшает) значение поля amount; операции бинарный +: добавляет к значению поля amount значение скаляра. Задание 4.Создать класс Rational для работы с рациональными дробями, содержащий следующие члены класса: 1. Поля: • int numerator; //числитель дроби • int denominator //знаменатель дроби 2. Функции, позволяющие: • вывести на экран дробь; • задать новые значения числителя и знаменателя дроби; • провести сокращение дроби; • сравнить две дроби. Кроме того, необходимо реализовать перегрузку операций сложения, умножения, вычитания и деления дробей. 24 Задание 5. Создать класс Complex для работы с комплексными числами (комплексное число определяется парой вещественных чисел: действительная часть, мнимая часть), содержащий следующие члены класса: 1. Поля: • double realPart; //действительная часть • double imaginaryPart; //мнимая часть 2. Функции, позволяющие: • вывести на экран комплексное число; • создать сопряженное число; • сравнить два числа на равенство; Кроме того, необходимо реализовать перегрузку операций сложения, умножения, вычитания и деления комплексных чисел. Задание 6. Создать класс Vector для работы с векторами на плоскости, содержащий следующие члены класса: 1. Поля: • int х, у; 2. Функции, позволяющие: • вывести на экран вектор; • вычислить длину вектора; • сравнить два вектора на равенство; Кроме того, необходимо реализовать перегрузку операций сложения, вычитания, скалярного и векторного произведения. Задание 7. Создать класс Vector3D для работы с векторами в пространстве, содержащий следующие члены класса: 1. Поля: • int х, у, z; 2. Функции, позволяющие: • вывести на экран вектор; • вычислить длину вектора; • сравнить два вектора на равенство; Кроме того, необходимо реализовать перегрузку операций сложения, вычитания, скалярного и векторного произведения. Задание 8. Создать класс для работы с одномерным массивом целых чисел. Разработать следующие члены класса: 1. Поле: • int [] intArray; 2. Функции, позволяющие: • ввести элементы массива с клавиатуры; • вывести элементы массива на экран; • отсортировать элементы массива в порядке возрастания; • узнать размерность массива; • вычислить сумму элементов в массиве. 25 Кроме того, необходимо реализовать перегрузку: • операции ++ (--), которая позволяет одновременно увеличить (уменьшить) значение всех элементов массива на 1; • операции бинарный *, которая позволяет домножить все элементы массива на скаляр. Задание 9. Создать класс для работы с двумерным массивом целых чисел. Разработать следующие члены класса: 1. Поле: • int [][] intArray; 2. Функции, позволяющие: • ввести элементы массива с клавиатуры; • вывести элементы массива на экран; • вычислить сумму элементов i-того столбца; • вычислить количество нулевых элементов в массиве; • установить значение всех элементов главной диагонали массива равное скаляру. Кроме того, необходимо реализовать перегрузку: • операции ++ (--) которая позволяет одновременно увеличить (уменьшить) значение всех элементов массива на 1; • операции бинарный + , которая позволяет сложить два массива соответствующих размерностей. Задание 10. Создать класс для работы со строками. Разработать следующие члены класса: 1. Поле: • string line; 2. Функции, позволяющие: • подсчитать количество цифр в строке; • выводить на экран все символы строки, встречающиеся в ней ровно один раз; • вывести на экран самую длинную последовательность повторяющихся символов в строке; • узнать общее количество символов в строке; • сравнить две строки на равенство. Кроме того, необходимо реализовать перегрузку: • операции унарного !, которая возвращает значение true, если строка не пустая, иначе false; • операции +, которая позволяет реализовать операцию слияния двух строк, т.е. получить новую строку, добавив в конец первой начало второй. Задание 11*. Создать класс Time, с полями hours, minutes, seconds, представляющими собой часы, минуты и секунды. Данный класс должен позволять выводить информацию о текущем времени, вычислять время через заданное количество часов, минут и секунд, а также вычислять время, прошедшее между двумя заданными моментами. Детальную структуру класса продумайте самостоятельно. 26 Задание 12*. Создать класс Data с полями year, month, day, представляющими собой год, месяц и день. Данный класс должен позволять выводить информацию о текущей дате, вычислять дату следующего дня, дату предыдущего дня, дату дня через заданное количество лет, месяцев и дней, а также вычислять временной отрезок (в годах, месяцах и днях) между двумя заданными датами. Детальную структуру класса продумайте самостоятельно. 4. НАСЛЕДОВАНИЕ 4.1. Основные понятия Наследование - это процесс создания новых классов (они называются производными классами или наследниками) на основе уже существующих классов (они называются базовыми). Производный класс наследует все члены базового класса, но также может их изменять и добавлять новые. Наследование позволяет использовать существующий код несколько раз. Описание производного класса выглядит следующим образом: class <имя производного класса> : [ <модификатор доступа> ] <имя базового класса> { <тело производного класса> }; При создании одиночных классов мы использовали два модификатора доступа public и private. При наследовании применяется еще один модификатор protected (защищенный). Защищенные элементы класса доступны только прямым наследникам и никому другому. Рассмотрим простое наследование классов на примере геометрических фигур. В качестве базового класса создадим класс Point (точка на плоскости), в качестве производного класса от Point класс PointSpace (точка в пространстве): #include #include using namespace std; class Point //базовый класс { public : int x; int y; void Show( string info) { cout << "Point " << info << ": (" << x << ", " << y << ")" << endl; } }; 27 class PointSpace : public Point //производный класс { public : int z; void Show( string info) { cout << "Point " << info << ":" ; cout << " (" << x << ", " << y << ", " << z << ")" << endl; } }; int main () { Point pointA; pointA.x = 0; pointA.у = 0; pointA.Show( "A" ); PointSpace pointB; pointB.x = 1; pointB.у = 1; pointB.z = 1; pointB.Show( "B" ); return 0; } Результат работы программы: Point A: (0, 0) PointB: (1, 1, 1) Таким образом, класс PointSpace унаследовал от класса Point поля х, у. В класс PointSpace было добавлено новое поле z, также в данном классе была переопределена функция Show. 4.2. Наследование конструкторов В иерархии классов как базовые, так и производные классы могут иметь собственные конструкторы. Если в базовом классе нет конструкторов, то в производном классе конструктор можно не описывать - он будет создан неявным образом. Если же в базовом классе конструктор задается явным образом, то производный класс должен иметь собственный конструктор, в котором явно вызывается конструктор базового класса. При этом конструктор базового класса создает часть объекта, соответствующую базовому классу, а конструктор производного класса — часть объекта, соответствующую производному классу. В предыдущем примере классы создавались за счет автоматического вызова средствами C++ конструктора по умолчанию. Теперь рассмотрим механизм наследования конструкторов на следующем примере: 28 #include #include using namespace std; class Point //базовый класс { protected : int x; int y; public : Point(): x(0), y(0) {} //конструктор без параметров Point( int x, int у): x(x), y(y) {} //конструктор с параметрами void Show( string info) { cout << "Point " << info << ":" ; cout << " (" << x << ", " << y << ")" << endl; } }; class PointSpace : public Point //производный класс { protected : int z; public : //наследование конструктора без параметров PointSpace(): Point (), z(0) {} //наследование конструктора с параметром PointSpace( int х, int у, int z): Point (x, y), z(z) {} void Show( string info) { cout << "Point " << info << ":" ; cout << " (" << x << ", " << y << ", " << z << ")" << endl; } }; int main() { Point pointA; pointA.Show( "A" ); Point pointB(1,1); pointB.Show( "B" ); PointSpace pointC; pointC.Show( "C" ); PointSpace pointD(2,2,2); pointD.Show( "D" ); return 0; } 29 Результат работы программы: Point А: (0, 0) Point В: (1, 1) Point С: (0, 0, 0) Point D: (2, 2, 2) 4.3. Виртуальные функции В языке C++ допустимо использовать указатель базового класса на объект производного класса. Например, следующим образом: Point *arrayPoint [4]; arrayPoint[0] = new Point (1, 1); arrayPoint[1] = new PointSpace (2, 2, 2); arrayPoint[2] = new PointSpace (3, 3, 3); arrayPoint[3] = new Point (4, 4); for ( int i = 0; i < 4; i++) { arrayPoint[i]->Show( "" ); //1 } Замечание. В данном примере мы используем динамическое распределение памяти, поэтому при обращении к члену-класса Show вместо символа «.» следует использовать символ «->» (см.строку 1) Также после завершения работы с созданной структурой array Point необходимо освободить выделенную память. Это можно сделать следующим образом: for ( int i = 0; i < 4; i++) { delete arrayPoint[i]; } В результате выполнения данного фрагмента программы мы ожидаем, что на экран будет выведено: Point: (1,1) Point: (2, 2, 2) Point: (3, 3, 3) Point: (4, 4) Вместо этого мы получим: Point: (1,1) Point: (2, 2) Point: (3, 3) Point: (4, 4) Дело в том, что все члены-функции класса заносятся в специальную таблицу функций. Это дает возможность хранить единственный экземпляр каждой функции и использовать его всем объектам одного класса совместно. Это решение вполне логично, так как если у нас есть 1000 объектов типа Point, то 1000 раз дублировать код функции Show нет никакого смысла. Для каждого класса строится собственная таблица функций. В нашем случае будет построено две таблицы - для класса Point и класса PointSpace. 30 В случае установки указателя типа Point на объект PointSpace возникает конфликт между двумя таблицами функций - базового класса и класса- потомка. Так как потомков у базового класса может быть много (в том числе, потомков третьего и более высоких уровней), то поиск по всем возможным таблицам требуемой функции является достаточно длительным процессом. Какая именно функция будет вызываться, определяется механизмом связывания вызывающего объекта с вызываемой функцией. В C++ все функции по умолчанию имеют раннее связывание, то есть компилятор и компоновщик решают, какая именно функция должна быть вызвана для заданного объекта, еще до запуска программы. В нашем случае arrayPoint объявлен типом Point, поэтому механизм раннего связывания для всех элементов данного массива вызывает функции класса Point. Избежать подобную проблему можно с помощью использования виртуальных функций. Виртуальная функция - это функция, которая объявлена в базовом классе с использованием ключевого слова virtual. Ключевое слово virtual достаточно написать один раз - при объявлении функции базового класса. Далее во всех производных классах данная функция будет считаться виртуальной. |