Справочное руководство по C++ - Бьярн Страустрап
Шрифт:
Интервал:
Закладка:
struct s { int a; }
void g()
{
struct* s p = new s; // обращение к глобальной `s'
p-›a = 1;
}
Имя считается описанным сразу же после появления его идентификатора в описании. Отсюда следует, что в описании
class A * A;
A в начале задается, как имя класса, а затем оно переопределяется как имя указателя на объект этого класса, поэтому для обозначения этого класса следует использовать спецификацию-сложного типа class A. Такое "трюкачество" с именами может вызвать недоумение, и лучше его избегать.
Конструкция имя-typedef (§R.7.1.3) обозначает класс и считается именем-класса, см. также §R.7.1.3.
R.9.2 Члены класса
список-членов:
описание-члена список-членов opt
спецификация-доступа : список-членов opt
описание-члена:
спецификации-описания opt список-описателей-членов opt ;
определение-функции ; opt
уточненное-имя ;
список-описателей-членов:
описатель-члена
список-описателей-членов , описатель-члена
описатель-члена:
описатель спецификация-чистой opt
идентификатор opt : выражение-константа
спецификация-чистой:
= 0
С помощью конструкции список-членов можно описать данные, функции, классы, элементы перечисления (§R.7.2), битовые поля, друзей (§R.11.4) и имена типов (§R.7.1.3, §R.9.1). Кроме того, список-членов может содержать описания, устанавливающие доступ к именам членов, см. §R.11.3. Никакой член не может быть дважды описан в списке-членов. Список-членов определяет все множество членов данного класса, т.е. нельзя добавить еще один член в каком-либо другом описании.
Отметим, что одно имя может обозначать несколько функций-членов при условии, что их типы достаточно отличаются друг от друга (§R.13). Укажем, что описатель-члена не может содержать инициализатора (§R.8.4). Инициализация члена возможна с помощью конструктора, см. §R.12.1.
Член не может иметь спецификацию auto, extern или register.
Конструкция спецификации-описания может отсутствовать только в описании функции. Конструкция список-описателей-членов может опускаться только после конструкций спецификация-класса, спецификация-перечисления или спецификация-описания, если последняя имеет вид friend спецификация-сложного-типа. Конструкция спецификация-чистой используется только при описании виртуальной функции (§R.10.2).
Если члены являются объектами классов, то эти классы должны быть ранее описаны. В частности, класс C1 не может содержать объект класса C1, но может содержать указатель или ссылку на класс C1. Если в типе нестатического члена используется массив, то все размеры всех индексов массива должны быть указаны.
Приведем простой пример описания класса:
struct tnode {
char tword[20];
int count;
tnode *left;
tnode *right;
};
Здесь класс содержит массив из двадцати символов, целое и два указателя на ту же структуру. После появления такого описания следующее:
tnode s, *sp;
задает s как объект типа tnode и sp как указатель на tnode. С учетом этих описаний s-›count обозначает член count структуры, на которую указывает sp; s.left обозначает указатель left на поддерево структуры s; s.right-›tword[0] обозначает первый символ члена tword поддерева структуры s, на которую указывает right.
Нестатические члены класса, представляющие данные и описанные подряд и без использования спецификации-доступа, размещаются внутри объекта типа класс так, что позже описанные члены имеют большие адреса. Порядок размещения таких членов, если их описание перемежается описаниями со спецификацией-доступа, зависит от реализации (§R.11.1). Принятые в реализации правила выравнивания могут привести к тому, что два соседних члена не будут располагаться сразу друг за другом. К этому же могут привести правила выделения памяти для виртуальных функций (§R.10.2) и виртуальных базовых классов (§R.10.1); см. также §R.5.4.
Функция-член (§R.9.3), имя которой совпадает с именем класса, является конструктором (§R.12.1). Имя статического члена данных, элемента перечисления, члена безымянного объединения или вложенного типа не может совпадать с именем класса.
R.9.3 Функции-члены
Функция, описанная как член (без спецификации friend §R.11.4), называется функция-член и вызывается в соответствии с синтаксисом члена класса (§R.5.2.4), например:
struct tnode {
char tword[20];
int count;
tnode *left;
tnode *right;
void set(char*, tnode* l, tnode *r);
};
Здесь set является функцией-членом и может вызываться так:
void f(tnode n1, tnode n2)
{
n1.set("abc",&n2,0);
n2.set("def",0,0);
}
Считается, что определение функции-члена принадлежит области видимости ее класса. Это означает, что в функции-члене (если она нестатическая, §R.9.4) можно непосредственно использовать имена членов ее класса. В статической функции-члене можно непосредственно использовать имена только статических членов, элементов перечисления и вложенных типов. Если определение функции-члена находится вне описания класса, ее имя следует уточнить именем класса с помощью операции ::, например:
void tnode::set(char* w, tnode* l, tnode* r)
{
count = strlen(w)+1;
if (sizeof(tword)‹=count)
error("tnode string too long");
strcpy(tword,w);
left = 1;
right = r;
}
Обозначение tnode::set указывает, что функция set является членом и находится в области видимости класса tnode. Имена членов tword, count, left и right относятся к членам того объекта, с именем которого вызывалась Поэтому в вызове n1.set("abc",&n2,0) tword обозначает n1.tword, а в вызове n2.set("def",0,0) tword обозначает n2.tword. Функции strlen, error и strcpy должны быть описаны где-то в программе.
Члены можно определять (§R.3.1) вне описания класса; если в описании класса они были описаны, но не определены, их не следует описывать заново, см. §R.3.3. После определения класса функции-члены этого класса можно использовать при описании друзей. Всякая вызываемая в программе функция-член должна иметь в точности одно определение.
Результат вызова нестатической функции-члена (§R.9.4) класса X, когда она вызывается не с объектом класса X, неопределен.
R.9.3.1 Указатель this
В нестатической (§R.9.3) функции-члене служебное слово this обозначает указатель на объект, с которым эта функция вызывалась. В функции-члене класса X тип this есть X *const, если только функция-член не описана со спецификацией const или volatile; для этих случаев this имеет тип const X *const или volatile X *const соответственно. Если функция описана с указанием const и volatile, то тип this будет const volatile X *const, см. также §R.18.3.3. Приведем пример:
struct s {
int a;
int f() const;
int g() { return a++; }
int h() const { return a++; } // ошибка
};
int s::f() const { return a; }
Операция a++ в теле функции s::h ошибочна, поскольку с ее помощью делается попытка изменить объект (часть его), с которым вызывалась функция s::h(). Это недопустимо для функции-члена, описанной со спецификацией const, т.к. this является указателем на const, иными словами, *this имеет спецификацию const.
Функция-член const (т.е. функция-член, описанная со спецификацией const) может вызываться как для объектов const, так и для объектов без спецификации const, тогда как функция-член без спецификации const может вызываться только для объектов без спецификации const, например:
void k(s& x, const s& y)
{
x.f();
x.g();
y.f();
y.g(); // ошибка
}
Здесь вызов y.g() является ошибкой, т.к. y есть const, а s::g() - функция-член без спецификации const, которая может изменять (и изменяет) объекты, для которых она вызывалась.
Аналогично, только функция-член volatile (т.е. функция-член, описанная со спецификацией volatile) может вызываться для объектов со спецификацией volatile. Функция-член может быть одновременно const и volatile.
Для объектов const или volatile могут вызываться конструкторы (§R.12.1) и деструкторы (§R.12.4). Конструкторы (§R.12.1) и деструкторы (§R.12.4) нельзя описывать со спецификациями const или volatile.
R.9.3.2 Функции-члены со спецификацией inline
Функцию-член можно определить (§R.8.3) в описании класса, в таком случае она считается подстановкой (inline, §R.7.1.2). Определять функцию в описании класса - это эквивалентно тому, чтобы описывать функцию и определять ее со спецификацией inline сразу же после описания класса. Считается, что такой перенос определения функции происходит после препроцессорной обработки до стадии синтаксического анализа и контроля типов. Поэтому программный фрагмент