
- •1 4 Семинар 1. Классы
- •Что принесло с собой ооп
- •1 6 Семинар 1. Классы
- •От структуры — к классу
- •Задача 1.1. Поиск в массиве структур
- •1 8 Семинар 1. Классы
- •Инициализаторы конструктора
- •28 Семинар 1. Классы
- •Конструктор копирования
- •Перегрузка операций инкремента
- •Задача 1.1. Поиск в массиве структур 31
- •1 Является переменной встроенного типа, форма инкремента безразлична:
- •Статические элементы класса
- •3 4 Семинар 1. Классы
- •4 0 Семинар 1. Классы
- •Задача 1.2. Реализация класса треугольников 45
Инициализаторы конструктора
Существует альтернативный способ инициализации отдельных частей объекта
в конструкторе — с помощью списка инициализаторов, расположенного после
двоеточия между заголовком и телом конструктора. Каждый инициализатор
имеет вид имя_поля (выражение) и обеспечивает инициализацию указанного поля
объекта значением указанного выражения. Если инициализаторов несколько, они
разделяются запятыми. Если полем объекта является объект другого класса, для
его инициализации будет вызван соответствующий конструктор.
Например, для класса Point, определяющего точку на плоскости в декартовой
системе координат:
class Point {
double X. у;
public:
Point (double _x = 0. double _y = 0) { x = _x: у = _y; }
/ / . . .
конструктор можно записать в альтернативной форме:
Point(double _х = 0. double _у = 0) : х(_х), у{_у) {}
Обратите внимание, что тело конструктора пустое, а действия по присваиванию
начальных значений полям перенесены в инициализаторы конструктора. В этом
примере может быть выбран любой вариант конструктора. Однако есть три ситуации,
в которых инициализация полей объекта возможна только с использованием
инициализаторов конструктора:
• для полей, являющихся объектами класса, в котором есть один или более
конструкторов, но отсутствует конструктор по умолчанию;
• для констант;
• для ссылок.
Обратите также внимание на то, что все параметры конструктора имеют значения
по умолчанию. Благодаря этому данный конструктор может использоваться
в двух формах: как конструктор с параметрами и как конструктор по умолчанию.
28 Семинар 1. Классы
Вообще говоря, инициализация элементов класса с помощью списка инициализаторов
является более эффективной по быстродействию, чем присваивание начальных
значений в теле конструктора. Например, если объявлен объект Point
р(1.5. 2.0), а в классе использован конструктор без списка инициализаторов, то
при создании объекта р его поля х и у будут сначала РП1ициализированы значением
О (стандартное требование C++), а затем в теле конструктора они получат
значения 1.5 и 2.0. ЕСЛРГ же в классе Point использован конструктор с ршициали-
заторами, то nppi создании объекта р его полям х и у сразу будут присвоены значения
1.5 и 2.0.
Конструктор копирования
Конструктор так же, как и остальные методы класса, можно перегружать. Одной
из важнейших форм перегружаемого конструктора является конструктор копирования
{сору constructor). Конструктор копирования вызывается в трех ситуациях:
• при инициализации нового объекта другим объектом в операторе объявления;
• при передаче объекта в функцию в качестве аргумента по значению;
• при возврате объекта из функции по значению.
Если при объявлении класса конструктор копирования не задан, компилятор C++
создает конструктор копирования по умолчанию, который просто дублирует объект,
осуществляя побайтное копрфование всех его полей. Однако при этом возможно
появление проблем, связанных с копированием указателей. Например, если объект,
передаваемый в функцию в качестве аргумента по значению, содержит некоторый
указатель рМет на выделенную область памяти, то в копии объекта этот указатель
будет ссылаться на ту же самую область памяти. При возврате из функции для
копии объекта будет вызван деструктор, освобождающий память, на которую
указывает рМет. При выходе из области видимости исходного объекта его деструктор
попытается еще раз освободить память, на которую указывает рМет. Результат
обычно весьма плачевный.
Для того чтобы исключить нежелательные побочные эффекты в каждой из трех
указанных ситуаций, необходимо создать конструктор копирования, в котором
реализуется необходимый контроль за созданием копии объекта.
Конструктор копирования имеет следующую общую форму:
имя_класса(const имя_класса & obj) {
/ / тело конструктора
}
Здесь obj — ссылка на объект, для которого должна создаваться КОПРШ. Например,
пусть имеется класс Соо, а у — объект типа Соо. Тогда следующие операторы
вызовут конструктор копирования класса Соо:
Соо X = у; //у явно инициализирует х
Funcl(y); // у передается в качестве аргумента по значению
у = Func2(); // у получает возвращаемый объект
Задача 1.1. Поиск в массиве структур 29
В двух первых случаях конструктору копирования передается ссылка на у. В последнем
случае конструктору копрфования передается ссылка на объект, возвращаемый
функцией Func2().
Пример реализации конструктора копирования будет показан при решении задачи
1.2.
Перегрузка операций
Любая операция^ определенная в C++, может быть перегружена для созданного
вами класса. Это делается с помощью функций специального вида, называемых
функциями-операциями (операторными функциями). Общий вид такой
функции:
возвращаемый_тип operator 7^ (список параметров) { тело функции }
где вместо знака # ставится знак перегружаемой операции.
Функция-операция может быть реализована либо как функция класса, либо как
внешняя (обычно дружественная) функция. В первом случае количество параметров
у функциР1-операции на единицу меньше, так как первым операндом при
этом считается сам объект, вызвавший данную операцию.
Например, покажем два варианта перегрузки операции сложения для класса Point:
Первый вариант — в форме метода класса:
class Point {
double X. у:
public:
//. . .
Point operator +(Poir1t&):
}:
Point Point::operator +(Point& p) {
return Point(x + p.x. у + p.у):
}
Второй вариант — в форме внешней глобальной функцтн!, причем функция, как
правило, объявляется дружественной классу, чтобы иметь доступ к его закрытым
э.яементам:
class Point {
double X. у:
public:
//. . .
friend Point operator +(Point&. Point&):
}:
Point operator +(Point& pi. Point& p2) {
return Point(pl.x + p2.x. pi.у + p2.y):
3a исключением «::», «?:>>, «.», «.*», «#», «##».
30 Семинар 1. Классы
Независимо от формы реализации операции «+» мы можем теперь написать:
Point рКО. 2). р2(-1. 5):
Point рЗ = р1 + р2:
Будет неплохо, если вы будете понимать, что, встретив выражение р1 + р2,
компилятор в случае первой формы перегрузки вызовет метод р1.operator +(р2),
а в случае второй формы перегрузки ~ глобальную функцию operator +(р1, р2).
Результатом выполнения данных операторов будет точка рЗ с координатами
X = -1, у = 7. Заметим, что для инициализации объекта рЗ будет вызван конструктор
копирования по умолчанию, но он нас устраивает, поскольку в классе нет
полей-указателей.
Если операция может перегружаться как внешней функцией, так и функцией
класса, какую из двух форм следует выбирать? Ответ: используйте перегрузку
в форме метода класса, если нет каких-либо причин, препятствующих этому.
Например, если первый аргумент (левый операнд) относится к одному из базовых
типов (к примеру, Int), то перегрузка операции возможна только в форме
внешней функции.