C++
Шрифт:
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; *);
X operator* (X,Y); int f(X);
X x = 1; Y y = x; int i = 2;
main (* i + 10; y + 10; y + 10 * y; x + y + i; x * x + i; f(7); f(y); y + y; 106 + y; *)
Определите X
8. (*2) Определите класс INT, который ведет себя в точности как int. Подсказка: определите INT::operator int.
9. (*1) Определите класс RINT, который ведет себя в точноти как int за исключением того, что единственные возмоные операции – это + (унарный и бинарный), – (унарный и бинарный), *, /, %. Подсказка: не определяйте INT::operator int.
10. (*3) Определите класс LINT, ведущий себя как RINT, за исключением того, что имеет точность не менее 64 бит.
11. (*4) Определите класс, который реализует арифметику с произвольной точностью. Подсказка: вам надо управлять памятью аналогично тому, как это делалось для класса string.
12. (*2) Напишите программу, доведенную до нечитаемого сотояния с помощью макросов и перегрузки операций. Вот идея: определите для INT + так, чтобы он означал -, и наоборот, а потом с помощью макроопределения определите int как INT. Переопределение часто употребляемых фунций, использование параметров ссылочного типа и несколко вводящих в заблуждение комментариев помогут устроить полную неразбериху.
13. (*3) Поменяйтесь со своим другом программами, которые у вас получились в предыдущем упражнении. Не запуская ее попытайтесь понять, что делает программа вашего друга. После выполнения этого упражнения вы будете знать, чего следует избегать.
14. (*2) Перепишите примеры с comlpex (#6.3.1), tiny (#6.3.2) и string (#6.9) не используя friend функций. Используйте только функции члены. Протестируйте каждую из новых версий. Сравните их с версиями, в которых используются функции друзья. Еще раз посмотрите Упражнение 5.3.
15. (*2) Определите тип vec4 как вектор их четырех float. Определите operator[] для vec4. Определите операции +, -, *, /, =, +=, -=, *=, /= для сочетаний векторов и чсел с плавающей точкой.
16. (*3) Определите класс mat4 как вектор из четырех vec4. Определите для mat4 operator[], возвращающий vec4. Опрделите для этого типа обычные операции над матрицами. Определите функцию, выполняющие для mat4 исключение Гусса.
17. (*2) Определите класс vector, аналогичный vec4, но с длиной, которая задается как параметр конструктора vector::vector(int).
18. (*3) Определите класс matrix, аналогичный mat4, но с размерностью, задаваемой параметрами конструктора matrix::matrix(int,int).
Глава 7
Производные классы
Не надо размножать объекты без необходимости
В этой главе описывается понятие производного класса в С ++. Производные классы дают простой, гибкий и эффективный апарат задания для класса альтернативного интерфейса и опредления класса посредством добавления возможностей к уже имещемуся классу без перепрограммирования или перекомпиляции. С помощью производных классов можно также обеспечить общий итерфейс для нескольких различных классов так, чтобы другие части программы
7.1 Введение
Представим себе процесс написания некоторого универсалного средства (например, тип связанный список, таблица имен или планировщик для системы моделирования), которое преднаначается для использования многими разными людьми в различных обстоятельствах. Очевидно, что в кандидатах на роль таких средств недостатка нет, и выгоды от их стандартизации огроны. Кажется, любой опытный программист написал (и отладил) дюжину вариантов типов множества, таблицы имен, сортирующей функции и т.п., но оказывается, что каждый программист и кадая программа используют свою версию этих понятий, из-за чего программы слишком трудно читать, тяжело отлаживать и сложно модифицировать. Более того, в большой программе вполне может быть несколько копий идентичных (почти) частей кода для рабты с такими фундаментальными понятиями.
Причина этого хаоса частично состоит в том, что предствить такие общие понятия в языке программирования сложно с концептуальной точки зрения, а частично в том, что средства, обладающие достаточной общностью, налагают дополнительные расходы по памяти и/или по времени, что делает их неудобными для самых простых и наиболее напряженно используемых средств (связанные списки, вектора и т.п.), где они были бы наиболее полезны. Понятие производного класса в С++, описываемое в #7.2, не обеспечивают общего решения всех этих проблем, но оно дает способ справляться с довольно небольшим числом ваных случаев. Будет, например, показано, как определить эффетивный класс обобщенного связанного списка таким образом, чтобы все его версии разделяли код.
Написание общецелевых средств – задача непростая, и чато основной акцент в их разработке другой, чем при разработке программ специального назначения. Конечно, нет четкой границы между средствами общего и специального назначения, и к метдам и языковым средствам, которые описываются в этой главе, можно относиться так, что они становятся все более полезны с ростом объема и сложности создаваемых программ.
7.2 Производные классы
Чтобы разделить задачи понимания аппарата языка и метдов его применения, знакомство с понятием производных классов делается в три этапа. Вначале с помощью небольших примеров, которые не надо воспринимать как реалистичные, будут описаны
сами средства языка (запись и семантика). После этого демонтрируются некоторые неочевидные применения производных класов, и, наконец, приводится законченная программа.
7.2.1 Построение производного класса
Рассмотрим построение программы, которая имеет дело с людьми, служащими в некоторой фирме. Структура данных в этой программе может быть например такой:
struct employee (* // служащий char* name; // имя short age; // возраст short department; // подразделение int salary; // жалование employee* next; // ... *);
Список аналогичных служащих будет связываться через поле next. Теперь давайте определим менеджера:
struct manager (* // менеджер employee emp; // запись о менеджере как о служащем employee* group; // подчиненные люди // ... *);