Приведем простой пример описания класса:
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 сразу же после описания класса. Считается, что такой перенос определения функции происходит после препроцессорной обработки до стадии синтаксического анализа и контроля типов. Поэтому программный фрагмент
int b;
struct x {
char* f() { return b; }
char* b;
};
эквивалентен
int b;
struct x {
char* f();
char* b;
};
inline char* x::f() { return b; } // перенос
Здесь в функции x::f() используется x::b, а не глобальное b.
Функции-члены можно определять даже в описании локальных или вложенных классов, где такой перенос будет синтаксически незаконным. Локальные классы обсуждаются в R.9.8, а вложенные классы в §R.9.7.
R.9.4 Статические члены
Для члена класса, представляющего данные или функцию, можно при описании класса задать спецификацию static. Для статического члена, представляющего данные, в программе существует только один экземпляр, которым владеют все объекты этого класса. Статический член не является частью объекта класса. Статические члены глобального класса подлежат внешнему связыванию (§R.3.3). Описание статического члена, представляющего данные, в описании класса не считается определением. Определение должно быть дано в другом месте, см. также. §R.18.3.
Статическая функция-член не имеет указатель this, поэтому для доступа к нестатическим членам своего класса она должна использовать операции . или -›. Статическая функция-член не может быть виртуальной. Недопустимы статические и нестатические функции-члены с одним именем и одинаковыми типами параметров.
Статические члены локального класса (§R.9.8) не подлежат связыванию и не могут определяться вне описания класса. Отсюда следует, что локальные классы не могут иметь статических членов, представляющих данные.
К статическому члену mem класса c1 можно обращаться как c1::mem (§R.5.1), т.е. независимо ни от какого объекта. К нему также можно обращаться с помощью операций доступа к членам . и -›. Если к статическому члену происходит обращение с помощью операций доступа, выражения, стоящие слева от . или -› не эквивалентны. Статический член mem существует даже, если не создано ни одного объекта класса c1. В примере ниже run_chain, idle и другие члены существуют даже, если не было создано ни одного объекта класса process: