Как и всякое другое языковое средство, перегрузка операций может
использоваться разумно и неразумно. В частности, возможностью
придавать новый смысл обычным операциям можно воспользоваться
так, что программа будет совершенно непостижимой. Представьте,
каково будет читателю, если в своей программе вы переопределили
операцию + так, чтобы она обозначала вычитание. Описанный здесь
механизм перегрузки будет защищать программиста и пользователя от
таких безрассудств. Поэтому программист не может изменить ни
смысл операций над основными типами данных, такими, как int, ни
синтаксис выражений и приоритеты операций для них.
По всей видимости перегрузку операций имеет смысл использовать
для подражания традиционному использованию операций. Запись с обычным
вызовом функции можно использовать в тех случаях, когда традиционной
записи с базовой операцией не существует, или, когда набор операций,
которые допускают перегрузку, не достаточен, чтобы записать с его
помощью нужные действия.
7.14 Упражнения
1. (*2) Определите итератор для класса string. Определите операцию
конкатенации + и операцию += , значащую "добавить в конец строки".
Какие еще операции вы хотели бы и смогли определить для этого
класса?
2. (*1.5) Определите для строкового класса операцию выделения подстроки
с помощью перегрузки ().
3. (*3) Определите класс string таким образом, чтобы операцию
выделения подстроки можно было применять к левой части
присваивания. Вначале напишите вариант, в котором строку можно
присваивать подстроке той же длины, а затем вариант с различными
длинами строк.
4. (*2) Разработайте класс string таким образом, чтобы объекты
его трактовались при передаче параметров и присваивании как
значения, т.е. чтобы в классе string копировались сами представления
строк, а не только управляющие структуры.
5. (*3) Измените класс string из предыдущего упражнения так, чтобы
строки копировались только при необходимости. Это значит, что
нужно хранить одно общее представления двух одинаковых строк до
тех пор, пока одна из них не изменится. Не пытайтесь задать операцию
выделения подстроки, которую одновременно можно применять и к
левой части присваивания.
6. (*4) Определите класс string, обладающий перечисленными в
предыдущих упражнениях свойствами: объекты его трактуются как
значения, копирование является отложенным (т.е. происходит только
при необходимости) и операцию выделения подстроки можно применять
к левой части присваивания.
7. (*2) Какие преобразования типа используются в выражениях следующей
программы?
struct X {
int i;
X(int);
operator+(int);
};
struct Y {
int i;
Y(X);
operator+(X);
operator int();
};
extern X operator*(X,Y);
extern int f(X);
X x = 1;
Y y = x;
int i = 2;
int main()
{
i + 10; y + 10; y + 10 * y;
x + y + i; x * X +i; f(7);
f(y); y + y; 106 + y;
}
Определите X и Y как целые типы. Измените программу так, чтобы
ее можно было выполнить и она напечатала значения всех
правильных выражений.
8. (*2) Определите класс INT, который будет эквивалентен типу int.
Подсказка: определите функцию INT::operator int().
9. (*1) Определите класс RINT, который будет эквивалентен типу int,
за исключением того, что допустимыми будут только операции:
+ (унарный и бинарный), - (унарный и бинарный), *, / и %.
Подсказка: не надо определять RINT::operator int().
10. (*3) Определите класс LINT, эквивалентный классу RINT, но в
нем для представления целого должно использоваться не менее 64
разрядов.
11. (*4) Определите класс, реализующий арифметику с произвольной
точностью. Подсказка: Придется использовать память так, как
это делается в классе string.
12. (*2) Напишите программу, в которой благодаря макрокомандам и
перегрузке будет невозможно разобраться. Совет: определите для
типа INT + как -, и наоборот; с помощью макроопределения задайте
int как INT. Кроме того, большую путаницу можно создать,
переопределяя широко известные функции, и используя параметры
типа ссылки и задавая вводящие в заблуждение комментарии.
13. (*3) Обменяйтесь решениями упражнения [12] с вашим другом.
Попробуйте понять, что делает его программа, не запуская ее. Если
вы сделаете это упражнение, вам станет ясно, чего надо избегать.
14. (*2) Перепишите примеры с классами complex ($$7.3), tiny
($$7.3.2) и string ($$7.11), не используя дружественные функции.
Используйте только функции-члены. Проверьте новые версии этих
классов. Сравните их с версиями, в которых используются
дружественные функции. Обратитесь к упражнению 5.3.
15. (*2) Определите тип vec4 как вектор из четырех чисел с плавающей
точкой. Определите для него функцию operator[]. Для комбинаций
векторов и чисел с плавающей точкой определите операции:
+, -, *, /, =, +=, -=, *= и /=.
16. (*3) Определите класс mat4 как вектор из четырех элементов типа
vec4. Определите для него функцию operator[], возвращающую vec4.
Определите для этого типа обычные операции с матрицами. Определите
в mat4 функцию, производящую преобразование Гаусса с матрицей.
17. (*2) Определите класс vector, аналогичный классу vec4, но здесь
размер вектора должен задаваться как параметр конструктора
vector::vector(int).
18. (*3) Определите класс matrix, аналогичный классу mat4, но здесь
размерности матрицы должны задаваться как параметры конструктора
matrix::matrix(int,int).
19. (*3) Завершите определение класса CheckedPtrToT из $$7.10 и
проверьте его. Чтобы определение этого класса было полным,
необходимо определить, по крайней мере, такие операции: *, ->,
=, ++ и --. Не выдавайте динамическую ошибку, пока действительно
не произойдет обращение по указателю с неопределенным значением.
20. (*1.5) Перепишите пример с программой подсчета слов из $$7.7
так, чтобы в ней не было заранее заданной максимальной длины
слова.
Достарыңызбен бөлісу: |