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

5.2.3 Ссылка на себя

В функции-члене можно непосредственно использовать имена членов того объекта, для которого она была вызвана:       class X {       int m;       public:       int readm() { return m; }       };       void f(X aa, X bb)       {       int a = aa.readm();       int b = bb.readm();       // ...       } При первом вызове readm() m обозначает aa.m, а при втором - bb.m.       У функции-члена есть дополнительный скрытый параметр, являющийся указателем на объект, для которого вызывалась функция. Можно явно использовать этот скрытый параметр под именем this. Считается, что в каждой функции-члене класса X указатель this описан неявно как       X *const this; и инициализируется, чтобы указывать на объект, для которого функция-член вызывалась. Этот указатель нельзя изменять, поскольку он постоянный (*const). Явно описать его тоже нельзя, т.к. this - это служебное слово. Можно дать эквивалентное описание класса X:       class X {       int m;       public:       int readm() { return this->m; }       }; Для обращения к членам использовать this излишне. В основном this используется в функциях-членах, непосредственно работающих с указателями. Типичный пример - функция, которая вставляет элемент в список с двойной связью:       class dlink {       dlink* pre; // указатель на предыдущий элемент       dlink* suc; // указатель на следующий элемент       public:       void append(dlink*);       // ...       };       void dlink::append(dlink* p)       {       p->suc = suc; // т.е. p->suc = this->suc       p->pre = this; // явное использование "this"       suc->pre = p; // т.е. this->suc->pre = p       suc = p; // т.е. this->suc = p       }       dlink* list_head;       void f(dlink* a, dlink* b)       {       // ...       list_head->append(a);       list_head->append(b);       }       Списки с такой общей структурой служат фундаментом списочных классов,       описываемых в главе 8. Чтобы присоединить звено к списку, нужно       изменить объекты, на которые настроены указатели this, pre и suc.       Все они имеют тип dlink, поэтому функция-член dlink::append() имеет       к ним доступ. Защищаемой единицей в С++ является класс, а не отдельный       объект класса.       Можно описать функцию-член таким образом, что объект, для которого       она вызывается, будет доступен ей только по чтению. Тот факт, что       функция не будет изменять объект, для которого она вызывается       (т.е. this*), обозначается служебным словом const в конце списка       параметров:       class X {       int m;       public:       readme() const { return m; }       writeme(int i) { m = i; }       };       Функцию-член со спецификацией const можно вызывать для постоянных       объектов, а функцию-член без такой спецификации - нельзя:       void f(X& mutable, const X& constant)       {       mutable.readme(); // нормально       mutable.writeme(7); // нормально       constant.readme(); // нормально       constant.writeme(7); // ошибка       }       В этом примере разумный транслятор смог бы обнаружить, что       функция X::writeme() пытается изменить постоянный объект. Однако,       это непростая задача для транслятора. Из-за раздельной       трансляции он в общем случае не может гарантировать "постоянство"       объекта, если нет соответствующего описания со спецификацией       const. Например, определения readme() и writeme() могли быть в       другом файле:       class X {       int m;       public:       readme() const;       writeme(int i);       };       В таком случае описание readme() со спецификацией const существенно.       Тип указателя this в постоянной функции-члене класса X есть       const X *const. Это значит, что без явного приведения с помощью this       нельзя изменить значение объекта:       class X {       int m;       public:       // ...       void implicit_cheat() const { m++; } // ошибка       void explicit_cheat() const { ((X*)this)->m++; }       // нормально       };       Отбросить спецификацию const можно потому, что понятие       "постоянства" объекта имеет два значения. Первое, называемое       "физическим постоянством" состоит в том, что объект хранится       в защищенной от записи памяти. Второе, называемое "логическим       постоянством" заключается в том, что объект выступает как       постоянный (неизменяемый) по отношению к пользователям. Операция       над логически постоянным объектом может изменить часть данных       объекта, если при этом не нарушается его постоянство       с точки зрения пользователя. Операциями, ненарушающими логическое       постоянство объекта, могут быть буферизация значений, ведение       статистики, изменение переменных-счетчиков в постоянных       функциях-членах.       Логического постоянства можно достигнуть приведением, удаляющим       спецификацию const:       class calculator1 {       int cache_val;       int cache_arg;       // ...       public:       int compute(int i) const;       // ...       };       int calculator1::compute(int i) const       {       if (i == cache_arg) return cache_val;       // нелучший способ       ((calculator1*)this)->cache_arg = i;       ((calculator1*)this)->cache_val = val;       return val;       }       Этого же результата можно достичь, используя указатель на данные       без const:       struct cache {       int val;       int arg;       };       class calculator2 {       cache* p;       // ...       public:       int compute(int i) const;       // ...       };       int calculator2::compute(int i) const       {       if (i == p->arg) return p->val;       // нелучший способ       p->arg = i;       p->val = val;       return val;       }       Отметим, что const нужно указывать как в описании, так и в определении       постоянной функции-члена. Физическое постоянство обеспечивается       помещением объекта в защищенную по записи память, только если в классе       нет конструктора ($$7.1.6).