Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Cpp_Страуструп.doc
Скачиваний:
16
Добавлен:
03.05.2015
Размер:
3.2 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.