Бьерн Страуструп. Язык программирования С++ Второе дополненное издание



Pdf көрінісі
бет101/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   97   98   99   100   101   102   103   104   ...   256
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; 


Бьерн Страуструп.
Язык программирования С++ 
 
125 
и инициализируется, чтобы указывать на объект, для которого функция-член вызывалась. Этот 
указатель нельзя изменять, поскольку он постоянный (*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); 
// 
ошибка 



Бьерн Страуструп.
Язык программирования С++ 
 
126 
В этом примере разумный транслятор смог бы обнаружить, что функция 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 { 


Бьерн Страуструп.
Язык программирования С++ 
 
127 
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). 


Достарыңызбен бөлісу:
1   ...   97   98   99   100   101   102   103   104   ...   256




©emirsaba.org 2024
әкімшілігінің қараңыз

    Басты бет