Задачи 190-196. Перегрузка операторов
190. Включите в класс Fraction операторы деления, получения обратной дроби, сокращения дроби. Выберите для этих действий подходящие символы операторов.
191. Добавьте в класс Complex три статических члена для подсчета количества вызовов каждого из трех конструкторов класса. Напечатайте значения данных счетчиков при завершении программы.
192. Замените три конструктора класса Complex одним конструктором с аргументами, имеющими значения по умолчанию:
Complex(double x = 0, double y = 0); //Конструктор
Проверьте работу программы.
193. Обратите внимание, что в класс комплексных чисел входят три оператора умножения: Complex на Complex, Complex на double и
double на Complex и только один оператор деления – Complex на Complex. Объясните, почему не возникает ошибки в инструкции
p3 = -p / 3;
в которой Complex делится на int. Сократите число операторов умножения.
194. Напишите функцию для решения квадратного уравнения с комплексными коэффициентами.
195. Напишите функцию для решения полного кубического уравнения
с комплексными коэффициентами. Подстановкой
,
получается каноническое уравнение
,
где
.
Каноническое уравнение решается по формулам Кардано
196. Напишите класс Integral, включающий указатель на функцию для вычисления некоторой математической функции и границы отрезка ее области определения. Предусмотрите в классе функцию-оператор для вычисления значения интеграла на заданном отрезке.
Глава 18. Конструктор копирования и оператор присваивания
Проблемы при копировании
Присваивание и копирование объектов уже рассматривалось в §15.7 на примере класса Date. Для простых классов, не содержащих указатели, проблем не возникает, достаточно встроенного почленного копирования одного объекта в другой объект. Посмотрим, что произойдет при выполнении копирования и инициализации для класса, включающего указатели на выделяемую в конструкторе класса память, на примере многоугольников из программы 48.
Приведем сокращенное объявление класса:
class Plgn // Класс многоугольников
{
int n; // Число вершин
float *x; float *y; // Указатели на массивы абсцисс и ординат
static int x0, y0; // Координаты центра
public:
Plgn(int n = 3); // Конструктор с аргументом по умолчанию
~Plgn() { delete [] x; delete [] y; } // Деструктор освобождает память
static void SetBegCoord(int xn, int yn) // Установка координат центра
{ x0 = xn; y0 = yn; }
};
# include <stdlib.h> // Доступ к random
Plgn::Plgn(int nn) // Конструктор
{…} // Здесь выделяется память под массивы абсцисс и ординат
Напишем функцию badcopy, которая создает и копирует многоугольники.
void badcopy()
{
Plgn P(5); // (1) Создание пятиугольника
Plgn T; // (2) Создание треугольника
Plgn Q = P; // (3) Создание копии Q пятиугольника P
T = P; // (4) Попытка превратить треугольник в пятиугольник
} // (5) Удаление Q, T, P
Рассмотрим манипуляции с памятью, которые выполняет badcopy, рис.43. На шаге (1) создается пятиугольник P со своей переменной n и своими указателями x, y, которые указывают на массивы памяти, выделенные в конструкторе. На шаге (2) создается треугольник T, имеющий свои n, x, y и свои массивы координат. На шаге (3) создается многоугольник Q, у которого n, x, y имеют те же значения, что и у многоугольника P. Указатели Q.x, Q.y указывают на ту же память, что и P.x, P.y. На шаге (4) за счет присваивания T.n, T.x, T.y получают такие же значения, как P.n, P.x, P.y. В результате возникли две проблемы.
Первая проблема состоит в том, что потеряна связь с памятью, выделенной треугольнику T. Память, выделяемая динамически оператором new, должна освобождаться явно оператором delete, в противном случае она все равно будет числится за программой, потерявшей с ней связь. Произойдет «утечка памяти».
Вторая проблема состоит в том, что теперь на одну и ту же область памяти ссылаются по три указателя. При завершении функции badcopy будут вызваны деструкторы объектов P, T, Q, которые три раза освободят одну и ту же память.
Рис.43. Работа с памятью функции badcopy
Можно попробовать выполнить следующую программу, использующую badcopy:
int Plgn::x0, Plgn::y0; // Определение статических членов класса
# include <conio.h>
void main()
{
Plgn::SetBegCoord(100, 100); // Установл. значен. статич. членов
badcopy();
}
Скорее всего ее выполнение приведет к системной ошибке.
Чтобы избежать указанных проблем, в классе, содержащем указатели, следует перегрузить оператор присваивания, в котором предусмотреть выделение памяти под объект-копию. Такие же действия нужно выполнить в конструкторе копирования, который используется при инициализации создаваемого объекта другим, ранее созданным объектом. Пример перегрузки оператора присваивания и создания конструктора копирования приведен в следующей программе.