Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Cpp_Страуструп.doc
Скачиваний:
17
Добавлен:
03.05.2015
Размер:
3.2 Mб
Скачать

7.9 Косвенное обращение

Операцию косвенного обращения к члену -> можно определить как унарную

постфиксную операцию. Это значит, если есть класс

class Ptr {

// ...

X* operator->();

};

объекты класса Ptr могут использоваться для доступа к членам класса

X также, как для этой цели используются указатели:

void f(Ptr p)

{

p->m = 7; // (p.operator->())->m = 7

}

Превращение объекта p в указатель p.operator->() никак не зависит от

члена m, на который он указывает. Именно по этой причине operator->()

является унарной постфиксной операцией. Однако, мы не вводим новых

синтаксических обозначений, так что имя члена по-прежнему должно

идти после -> :

void g(Ptr p)

{

X* q1 = p->; // синтаксическая ошибка

X* q2 = p.operator->(); // нормально

}

Перегрузка операции -> прежде всего используется для создания

"хитрых указателей", т.е. объектов, которые помимо использования как

указатели позволяют проводить некоторые операции при каждом обращении

к указуемому объекту с их помощью. Например, можно определить класс

RecPtr для организации доступа к объектам класса Rec, хранимым на

диске. Параметром конструктора RecPtr является имя, которое будет

использоваться для поиска объекта на диске. При обращении к объекту

с помощью функции RecPtr::operator->() он переписывается в основную

память, а в конце работы деструктор RecPtr записывает измененный

объект обратно на диск.

class RecPtr {

Rec* in_core_address;

const char* identifier;

// ...

public:

RecPtr(const char* p)

: identifier(p) { in_core_address = 0; }

~RecPtr()

{ write_to_disc(in_core_address,identifier); }

Rec* operator->();

};

Rec* RecPtr::operator->()

{

if (in_core_address == 0)

in_core_address = read_from_disc(identifier);

return in_core_address;

}

Использовать это можно так:

main(int argc, const char* argv)

{

for (int i = argc; i; i--) {

RecPtr p(argv[i]);

p->update();

}

}

На самом деле, тип RecPtr должен определяться как шаблон типа

(см. $$8), а тип структуры Record будет его параметром. Кроме

того, настоящая программа будет содержать обработку ошибок и

взаимодействие с диском будет организовано не столь примитивно.

Для обычных указателей операция -> эквивалентна операциям,

использующим * и []. Так, если описано

Y* p;

то выполняется соотношение

p->m == (*p).m == p[0].m

Как всегда, для определенных пользователем операций такие соотношения

не гарантируются. Там, где все-таки такая эквивалентность требуется,

ее можно обеспечить:

class X {

Y* p;

public:

Y* operator->() { return p; }

Y& operator*() { return *p; }

Y& operator[](int i) { return p[i]; }

};

Если в вашем классе определено более одной подобной операции,

разумно будет обеспечить эквивалентность, точно так же, как разумно

предусмотреть для простой переменной x некоторого класса, в котором

есть операции ++, += = и +, чтобы операции ++x и x+=1 были

эквивалентны x=x+1.

Перегрузка -> как и перегрузка [] может играть важную роль для

целого класса настоящих программ, а не является просто экспериментом

ради любопытства. Дело в том, что в программировании понятие

косвенности является ключевым, а перегрузка -> дает ясный, прямой

и эффективный способ представления этого понятия в программе.

Есть другая точка зрения на операцию ->, как на средство задать

в С++ ограниченный, но полезный вариант понятия делегирования

(см. $$12.2.8 и 13.9).