Может быть, сделать указатели закрытыми, чтобы только функции-члены класса могли обращаться к ним? Попробуем.
class Link {
public:
string value;
Link(const string& v,Link* p = 0,Link* s = 0)
:value(v), prev(p),succ(s) { }
Link* insert(Link* n); // вставляет n перед данным объектом
Link* add(Link* n); // вставляет n после данного объекта
Link* erase; //
удаляет данный объект из списка
Link* find(const string& s); // находит s в списке
Link* advance(int n) const; // удаляет n позиций
// из списка
Link* next const { return succ; }
Link* previous const { return prev; }
private:
Link* prev;
Link* succ;
};
Этот фрагмент выглядит многообещающе. Мы определили операции, не изменяющие состояние объекта класса
Link
, с помощью константных функций-членов. Мы добавили (не модифицирующие) функции
next
и
previous
, чтобы пользователи могли перемещаться по списку, — поскольку непосредственный доступ к указателям
succ
и
prev
теперь запрещен. Мы оставили значение узла в открытом разделе класса, потому что (пока) у нас не было причины его скрывать; ведь это просто данные.
Попробуем теперь реализовать функцию
Link::insert
, скопировав и модифицировав предыдущий вариант.
Link* Link::insert(Link* n) // вставляет n перед p; возвращает n
{
Link* p = this; // указатель на данный объект
if (n==0) return p; // ничего не вставляем
if (p==0) return n; // ничего не вставляем
n–>succ = p; // p следует за n
if (p–>prev) p–>prev–>succ = n;
n–>prev = p–>prev; // предшественник p становится
// предшественником n
p–>prev = n; // n становится предшественником p
return n;
}
Как получить указатель на объект, для которого была вызвана функция
Link::insert
? Без помощи языка это сделать невозможно. Однако в каждой функции-члене существует идентификатор
this
, являющийся указателем на объект, для которого она вызывается. A в качестве альтернативы мы могли бы просто писать
this
вместо
p
.
Link* Link::insert(Link* n) // вставляет n перед p; возвращает n
{
if (n==0) return this;
if (this==0) return n;
n–>succ = this; // этот объект следует за n
if (this–>prev) this–>prev–>succ = n;
n–>prev = this–>prev; // предшественник этого объекта
// становится
//
предшественником объекта n
this–>prev = n; // n становится предшественником этого
// объекта
return n;
}
Это объяснение выглядит немного многословным, но мы не обязаны упоминать, что указатель
this
обеспечивает доступ к члену класса, поэтому код можно сократить.
Link* Link::insert(Link* n) // вставляет n перед p; возвращает n
{
if (n==0) return this;
if (this==0) return n;
n–>succ = this; // этот объект следует за n
if (prev) prev–>succ = n;
n–>prev = prev; // предшественник этого объекта
// становится
// предшественником объекта n
prev = n; // n становится предшественником этого
// объекта
return n;
}
Иначе говоря, при каждом обращении к члену класса происходит неявное обращение к указателю
this
. Единственная ситуация, в которой его необходимо упомянуть явно, возникает, когда нужно сослаться на весь объект.
Обратите внимание на то, что указатель
this
имеет специфический смысл: он ссылается на объект, для которого вызывается функция-член. Он не указывает на какой-то из ранее использованных объектов. Компилятор гарантирует, что мы не сможем изменить значение указателя
this
в функции-члене. Рассмотрим пример.
struct S {
// ...
void mutate(S* p)
{
this = p; // ошибка: указатель this не допускает изменений
// ...
}
};
17.10.1. Еще раз об использовании списков
Сталкиваясь с вопросами реализации, мы можем увидеть, как выглядит использование списка.