Итоговый тест№9 Перегрузку операций. Теория
Перегрузка операции — это специфическая перегрузка функции, которая позволяет использовать операции с объектами пользовательских классов. При перегрузке операций их функционал и назначение следует сохранять максимально приближенно к их первоначальному применению. Если суть применяемого операции с объектами пользовательских классов интуитивно не понятна, то лучше использовать функцию с именем, вместо перегрузки операции.
Операции могут быть перегружены через обычные функции, через дружественные функции и через методы класса. Следующие правила помогут сориентироваться, какой способ перегрузки и когда следует использовать:
Перегрузку операций присваивания (=), индекса ([]), вызова функции (()) или выбора члена (->) выполняйте через методы класса.
Перегрузку унарных операций выполняйте через методы класса.
Перегрузку бинарных операций, которые изменяют свой левый операнд (например, оператор +=) выполняйте через методы класса.
Перегрузку бинарных операций, которые не изменяют свой левый операнд (например, оператор +) выполняйте через обычные или дружественные функции.
Перегрузка операций преобразования типов данных используется для явного или неявного преобразования объектов пользовательского класса в другой тип данных.
Конструктор копирования — это особый тип конструктора, используемый для инициализации объекта другим объектом того же класса. Конструкторы копирования используются в прямой/uniform-инициализации объектов объектами того же типа, копирующей инициализации (Fraction f = Fraction (7,4)) и при передаче или возврате параметров по значению.
Если вы не предоставите свой конструктор копирования, то компилятор автоматически его предоставит. Конструкторы копирования по умолчанию (предоставляемые компилятором) используют почленную инициализацию. Это означает, что каждый член объекта копии инициализируется соответствующим членом исходного объекта. Конструктор копирования может быть проигнорирован компилятором в целях оптимизации, даже если он имеет побочные эффекты, поэтому сильно не полагайтесь на свой конструктор копирования.
Конструкторы считаются конструкторами преобразования по умолчанию. Это означает, что компилятор будет использовать их для неявной конвертации объектов других типов данных в объекты вашего класса. Вы можете избежать этого, используя ключевое слово explicit. Вы также можете удалить функции внутри своего класса, включая конструктор копирования и перегруженный оператор присваивания, если это необходимо. И если позже в программе будет вызываться удаленная функция, то компилятор выдаст ошибку.
Операция присваивания может быть перегружен для выполнения операций присваивания с объектами вашего класса. Если вы не предоставите перегруженную операцию присваивания сами, то компилятор создаст его за вас. Перегруженные операции присваивания всегда должны иметь проверку на самоприсваивание.
По умолчанию операция присваивания и конструктор копирования, предоставляемые компилятором, выполняют почленную инициализацию/присваивание, что является поверхностным копированием. Если в вашем классе есть динамически выделенные члены, то это, скорее всего, приведет к проблемам (несколько объектов могут указывать на одну и ту же выделенную память). В таком случае вам нужно будет явно определить свой конструктор копирования и перегрузку операции присваивания для выполнения глубокого копирования.
Тест
Задание №1
Предположим, что Square — это класс, а square — это объект этого класса. Какой способ перегрузки лучше использовать для следующих операторов?
square + square
-square
std::cout << square
square = 7;
Задание №2
Напишите класс Average, который будет вычислять среднее значение всех передаваемых ему целых чисел. Используйте два члена: первый должен быть типа int32_t и использоваться для вычисления суммы всех передаваемых чисел, второй должен быть типа int8_t и использоваться для вычисления количества передаваемых чисел. Чтобы найти среднее значение, нужно разделить сумму на количество.
a) Следующий код функции main ():
int main () {
Average avg;
avg += 5;
std::cout << avg << '\n'; // 5 / 1 = 5
avg += 9;
std::cout << avg << '\n'; // (5 + 9) / 2 = 7
avg += 19;
std::cout << avg << '\n'; // (5 + 9 + 19) / 3 = 11
avg += -9;
std::cout << avg << '\n'; // (5 + 9 + 19 - 9) / 4 = 6
(avg += 7) += 11; // выполнение цепочки операций
std::cout << avg << '\n'; // (5 + 9 + 19 - 9 + 7 + 11) / 6 = 7
Average copy = avg;
std::cout << copy << '\n';
return 0;
}
Должен выдавать следующий результат:
5
7
11
6
7
7
b) Требуется ли этому классу явный конструктор копирования или операция присваивания?
Задание №3
Напишите свой собственный класс-массив целых чисел IntArray (не используйте std::array или std::vector). Пользователи должны передавать размер массива при создании объекта этого класса, а сам массив (переменная-член) должен выделяться динамически. Используйте макрос assert для проверки передаваемых значений, а также свой конструктор копирования и перегрузку операции присваивания, если это необходимо, чтобы следующий код:
#include <iostream>
IntArray fillArray() {
IntArray a (6);
a[0] = 6;
a[1] = 7;
a[2] = 3;
a[3] = 4;
a[4] = 5;
a[5] = 8;
return a;
}
int main () {
IntArray a = fillArray ();
std::cout << a << '\n';
IntArray b (1);
a = a;
b = a;
std::cout << b << '\n';
return 0;
}
Выдавал следующий результат:
6 7 3 4 5 8
6 7 3 4 5 8
