Глава 9. Итоговый тест

   ⁄ 

 Обновлено 29 мая 2018  ⁄ 

⁄   445

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

Теория

Перегрузка оператора — это специфическая перегрузка функции, которая позволяет использовать операторы с объектами пользовательских классов. При перегрузке операторов их функционал и назначение следует сохранять максимально приближенно к их первоначальному применению. Если суть применяемого оператора с объектами пользовательских классов интуитивна не понятна, то лучше использовать функцию с именем, вместо перегрузки оператора.

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

  Перегрузку операторов присваивания (=), индекса ([]), вызова функции (()) или выбора члена (->) выполняйте через методы класса.

  Перегрузку унарных операторов выполняйте через методы класса.

  Перегрузку бинарных операторов, которые изменяют свой левый операнд (например, оператор +=) выполняйте через методы класса.



  Перегрузку бинарных операторов, которые не изменяют свой левый операнд (например, оператор +) выполняйте через обычные или дружественные функции.

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

Конструктор копирования — это особый тип конструктора, используемый для инициализации объекта другим объектом того же класса. Конструкторы копирования используются в прямой/uniform инициализации объектов объектами того же типа, копирующей инициализации (Fraction f = Fraction(7,4)) и при передаче или возврате параметров по значению.

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

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

Оператор присваивания может быть перегружен для выполнения операций присваивания с объектами вашего класса. Если вы не предоставите перегруженный оператор присваивания сами, то компилятор создаст его за вас. Перегруженные операторы присваивания всегда должны иметь проверку на самоприсваивание.

По умолчанию оператор присваивания и конструктор копирования, предоставляемые компилятором, выполняют почленную инициализацию/присваивание, что является поверхностным копированием. Если в вашем классе есть динамически выделенные члены, то это, скорее всего, приведет к проблемам (несколько объектов могут указывать на одну и ту же выделенную память). В таком случае вам нужно будет явно определить свой конструктор копирования и перегрузку оператора присваивания для выполнения глубокого копирования.

Тест

Задание №1

Предположим Square — это класс, а square — это объект этого класса. Какой способ перегрузки лучше использовать для следующих операторов?

  square + square

  -square

  std::cout << square

  square = 7;

Ответ 1

  Перегрузку бинарного оператора + (плюс) лучше всего выполнить через обычную/дружественную функцию.



  Перегрузку унарного оператора – (минус) лучше всего выполнить через метод класса.

  Перегрузка оператора << должна выполняться через обычную/дружественную функцию.

  Перегрузка оператора = должна выполняться через метод класса.

Задание №2

Напишите класс Average, который будет вычислять среднее значение всех передаваемых ему целых чисел. Используйте два члена: первый должен быть типа int32_t и использоваться для вычисления суммы всех передаваемых чисел. Второй должен быть типа int8_t и использоваться для вычисления количества передаваемых чисел. Чтобы найти среднее значение нужно разделить сумму на количество.

a) Следующий код функции main():

должен производить следующий результат:

5
7
11
6
7
7

Ответ а)

b) Требуется ли этому классу явный конструктор копирования или оператор присваивания?

Ответ b)

Нет. Использование конструктора копирования и перегруженного оператора присваивания, предоставляемых компилятором по умолчанию, здесь будет достаточно.

Задание №3

Напишите свой собственный класс-массив целых чисел IntArray (не используйте std::array или std::vector). Пользователи должны передавать размер массива при создании объекта этого класса, а сам массив (переменная-член) должен выделяться динамически. Используйте стейтменты assert для проверки передаваемых значений и свой конструктор копирования и перегрузку оператора присваивания, если это необходимо, чтобы следующий код:

производил следующий результат:

6 7 3 4 5 8
6 7 3 4 5 8

Ответ 3

Задание №4

Значение типа с плавающей запятой — это число с десятичной дробью, где количество цифр после запятой (дробная часть) может меняться. Значение типа с фиксированной запятой — это число с дробью, где дробь (после запятой) фиксированная.

Вам нужно написать класс для реализации значений типа с фиксированной запятой с двумя цифрами после запятой (например, 11.47, 5.00 или 1465.78). Диапазон класса должен быть от -32768.99 до 32767.99, в дробной части могут быть любые две цифры, проблем с точностью – не допустить.

a) Какого типа данных переменную-член следует использовать для реализации значений типа с фиксированной запятой с 2 цифрами после запятой? (Обязательно прочитайте ответ, прежде чем приступать к выполнению следующего задания)

Ответ а)

Существует несколько способов реализации значений типа с фиксированной запятой. Поскольку это тот же тип с плавающей запятой (кроме того, что количество цифр после запятой — фиксированное), то использование типа float или double может показаться очевидным решением. Но значения типа с плавающей запятой имеют проблемы с точностью. С фиксированной запятой мы можем перебрать все возможные числа, которые могут находиться в дробной части значения (в нашем случае, от .00 до .99), поэтому использование типа данных с ошибками в точности не является хорошим выбором.

Лучшее решение: использовать тип int16_t signed для хранения целой части значения и int8_t signed для хранения дробной части значения.

b) Напишите класс FixedPoint, который реализует рекомендуемое решение из предыдущего задания. Если дробная или целая части значения являются отрицательными, то число должно рассматриваться как отрицательное. Реализуйте перегрузку необходимых операторов и напишите необходимые конструкторы, чтобы следующий код функции main():

производил следующий результат:

37.58
-3.09
-4.07
-5.07
-0.03
-0.03

Подсказка: Для вывода значения – конвертируйте его в double, используя оператор cast.

Ответ b)

c) Теперь добавьте конструктор, который будет принимать значение типа double. Вы можете округлить целую часть (слева от запятой) с помощью функции round() (которая находится в заголовочном файле cmath).

Подсказка 1: Вы можете получить целую часть от числа double путем конвертации double в тип int.

Подсказка 2: Для перемещения одной цифры влево от запятой — используйте умножение на 10. Для перемещения двух цифр — используйте умножение на 100.

Следующий код функции main():

должен производить следующий результат:

0.03
-0.03
4.01
-4.01

Ответ c)

d) Выполните перегрузку следующих операторов: ==, >>, — (унарный) и + (бинарный).

Следующая программа:

должна производить следующий результат:

true
true
true
true
true
true
true
true
-0.48
0.48
Enter a number: 5.678
You entered: 5.68

Подсказка: Для выполнения перегрузки оператора >&gt; используйте конструктор с параметром double для создания анонимного объекта класса FixedPoint, а затем присвойте этот объект параметру функции перегрузки оператора >>.

Ответ d)

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (4 оценок, среднее: 5,00 из 5)
Загрузка...
Подписаться на обновления:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

ПОДПИСЫВАЙТЕСЬ

НА КАНАЛ RAVESLI В TELEGRAM

@ravesli

ПОДПИСАТЬСЯ БЕСПЛАТНО