Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КРАТКИЙ ОБЗОР С.doc
Скачиваний:
1
Добавлен:
26.10.2018
Размер:
2.11 Mб
Скачать

7.10 Инкремент и декремент

Если мы додумались до "хитрых указателей", то логично попробовать переопределить операции инкремента ++ и декремента -- , чтобы получить для классов те возможности, которые эти операции дают для встроенных типов. Такая задача особенно естественна и необходима, если ставится цель заменить тип обычных указателей на тип "хитрых указателей", для которого семантика остается прежней, но появляются некоторые действия динамического контроля. Пусть есть программа с распространенной ошибкой:       void f1(T a) // традиционное использование       {       T v[200];       T* p = &v[10];       p--;       *p = a; // Приехали: `p' настроен вне массива,       // и это не обнаружено       ++p;       *p = a; // нормально       } Естественно желание заменить указатель p на объект класса CheckedPtrToT, по которому косвенное обращение возможно только при условии, что он действительно указывает на объект. Применять инкремент и декремент к такому указателю будет можно только в том случае, что указатель настроен на объект в границах массива и в результате этих операций получится объект в границах того же массива:       class CheckedPtrToT {       // ...       };       void f2(T a) // вариант с контролем       {       T v[200];       CheckedPtrToT p(&v[0],v,200);       p--;       *p = a; // динамическая ошибка:       // `p' вышел за границы массива       ++p;       *p = a; // нормально       } Инкремент и декремент являются единственными операциями в С++, которые можно использовать как постфиксные и префиксные операции. Следовательно, в определении класса CheckedPtrToT мы должны предусмотреть отдельные функции для префиксных и постфиксных операций инкремента и декремента:       class CheckedPtrToT {       T* p;       T* array;       int size;       public:       // начальное значение `p'       // связываем с массивом `a' размера `s'       CheckedPtrToT(T* p, T* a, int s);       // начальное значение `p'       // связываем с одиночным объектом       CheckedPtrToT(T* p);       T* operator++(); // префиксная       T* operator++(int); // постфиксная       T* operator--(); // префиксная       T* operator--(int); // постфиксная       T& operator*(); // префиксная       }; Параметр типа int служит указанием, что функция будет вызываться для постфиксной операции. На самом деле этот параметр является искусственным и никогда не используется, а служит только для различия постфиксной и префиксной операции. Чтобы запомнить, какая версия функции operator++ используется как префиксная операция, достаточно помнить, что префиксной является версия без искусственного параметра, что верно и для всех других унарных арифметических и логических операций. Искусственный параметр используется только для "особых" постфиксных операций ++ и --.       С помощью класса CheckedPtrToT пример можно записать так:       void f3(T a) // вариант с контролем       {       T v[200];       CheckedPtrToT p(&v[0],v,200);       p.operator--(1);       p.operator*() = a; // динамическая ошибка:       // `p' вышел за границы массива       p.operator++();       p.operator*() = a; // нормально       } В упражнении $$7.14 [19] предлагается завершить определение класса CheckedPtrToT, а другим упражнением ($$9.10[2]) является преобразование его в шаблон типа, в котором для сообщений о динамических ошибках используются особые ситуации. Примеры использования операций ++ и -- для итераций можно найти в $$8.8.