46019 (665326), страница 17
Текст из файла (страница 17)
Опциональный список-компонентов объявляет компоненты класса (данные и функции) для имени-класса с умолчаниями и переопределениями спецификаторов доступа, которые могут влиять на то, какие функции к каким компонентам класса могут иметь доступ.
Имена классов
Имя-класса это любой идентификатор, уникальный в пределах своего контекста. В классах структур и в объединениях имя-класса можетбыть опущено (см. "Структуры и типы, определяемые пользователем (typedef) без тегов" на стр.65 оригинала).
Типы классов
Объявление создает уникальный тип, тип класса
"имя-класса". Это позволяет вам объявлять последующие объекты класса (или вхождения класса) данного типа, а также объекты, являющиеся производными от этого типа (такие как указатели, ссылки, массивы объектов "имя-класса" и т.д.):
class X (* ... *);
X x, &xr, *xptr, xarray[10];
/* четыре объекта: типа X, ссылка на X, указатель на X и масив элементов типа X */
struct Y (* ... *);
Y y, &yr, *yptr, yarray[10];
// С позволяет иметь
// struct Y y, &yr, *yptr, yarray[10];
union Z (* ... *);
Z z, &zr, *zptr, zarray[10];
// С позволяет иметь
// union Z z, &zr, *zptr, zarray[10];
Отметим различие между объявлением структур и объединений в С и С++: в С ключевые слова struct и union обязательны, но в С++ они нужны только в том случае, когда имена классов, Y и Z, скрыты (см. следующий раздел).
Контекст имени класса
Контекст имени класса является локальным, с некоторыми особенностями, характерными для классов. Контекст имени класса начинается с точки его объявления и заканчивается вместе с объемлющим блоком. Имя класса скрывает любой класс, объект, нумератор или функцию с тем же именем в объемлющем контексте. Еслиимя класса объявлено в контексте, содержащем объявление объекта, функции или нумератора с тем же именем, обращение к классу возможно только при помощи уточненного спецификатора типа. Это означает, что с именем класса нужноиспользовать ключ класса, class, struct или union. Например,
struct S (* ... *);
int S(struct S *Sptr);
void func(void)
(*
S t; // недопустимое объявление: нет ключа класса // и функции S в контексте struct S s; // так можно: есть уточнение ключем класса
S(&s); // так можно: это вызов функции
*)
С++ позволяет также неполное объявление класса:
class X;// еще нет компонентов !
struct Y;
union Z;
Неполные объявления позволяют некоторые ссылки к именам классов X, Y или Z (обычно ссылки на указатели объектов классов) до того, как классы будут полностью определены (см. "Объявление компонентов структуры" на стр.65 оригинала). Разумеется, прежде чем вы сможете объявить и использовать объекты классов, вы должны выполнить полное объявление классов со всеми их компонентами.
Объекты классов
Объекты классов могут быть присвоены (если не было запрещено копирование), переданы как аргументы функции, возвращены фунукцией (за некоторыми исключениями) и т.д. Прочие операции с объектами и компонентами классовмогут быть различными способами определены пользователем, включая функциикомпоненты и "друзья", а также переопределение стандартных функций и операций при работе с объектами конкретного класса. Переопределение функций и операций называется перегрузкой. Операции и функции, ограниченные объектами конкретного класса (или взаимосвязанной группы классов) называются функциям-компонентами данного класса. С++ имеет механизм, позволяющий вызвать то же имя функции или операции для выполнения другой задачи, в зависимости от типа или числа аргументов или операндов.
Список компонентов класса
Опциональный список-компонентов представляет собой последовательность объявлений данных (любого типа, включая нумераторы,битовыеполя и другие классы) и объявлений и определений функций, каждое из которых может иметьопциональные спецификаторы класса памяти и модификаторы доступа. Определенные таким образом объекты называются компонентами класса. Спецификаторы класса памяти auto, extern и register в данном случвае недопустимы. Компоненты могут быть объявлены со спецификаторами класса памяти static.
Функции-компоненты
Функция, объявленная без спецификатораfriend, называется функцией-компонентом класса. Функции, объявленные с модификатором friend, называется "функцией-другом".
Одно и то же имя может использоваться для обозначения более чем одной функции, при условии, что они отличны по типам или числу аргументов.
Ключевое слово this
Не-статические функции компонентов работают с объектами типа класса, с которыми они вызываются. Например, если xэто объект класса X, а f это функция-компонент X, то вызов функци x.f() работает с x. Аналогичным образом, если xptr есть указатель объекта X, то вызов функции xptr->() работает с *xptr. Откуда f может знать, с каким x работать? С++ передает f указатель на x, называемый this. this передается как скрытый аргумент при вызове не-статических функций-компонентов.
Ключевоеслово this представляет собой локальную переменную, доступную в теле не-статической функции-компонента. this не требует объявлений, и на него редко встречаются явные ссылки в определении функции. Однако, оно неявно используется в функции для ссылки ккомпонентам. Если, например, вызывается x.f(y), где y есть компонент X, то this устанавливается на &x, а y устанавливается на this->y, что эквивалентно x.y.
Встраиваемые функции (inline)
Функция-компонента может быть объявленав пределах своего класса, но определена где-либо в другом месте. И наоборот, функция-компонента может быть и объявлена, и определена в своем классе, и тогда она называется встраиваемой функцией. (Некоторые примеры таких функций показаны в главе 5 документа "Начало работы").
В некоторых случаях Turbo C++ может уменьшить затраты времени на вызов функции, подстанавливая вместо вызова функции непосредственно компилированный код тела функции. Этот процесс, называемый встраиваемым расширением тела функции, не влияет на контекст имени функции или ее аргументов. Встраиваемое расширение не всегда полезно и желательно. Спецификатор inline представляет собой запрос (или требование) компилятору, в котором вы сообщаете, что встраиваемые расширения желательны. Как и в случае спецификатора класса памяти register, компилятор может либо удовлетворить, либо проигнорировать ваше пожелание.
Явные или неявные запросы inlineлучше всего резервировать для небольших по объему и часто вызываемых функций, таких как функции типа operator, реализующих перегруженные операторы. Например, следующее объявление класса:
int i; // global int
class X (*
public:
char* func(void) (* return i; *) // inline по умолчанию char* i;
*);
эквивалентно
inline char* X::func(void) (*return i; *)
func определяется "вне" класса с явным спецификатором inline. Переменная i, возвращаемая func, есть char* i класса X - см. раздел, посвященный контексту компонентов на стр.107 оригинала.
Статические компоненты
Спецификатор класса памяти static может быть использован в объявлениях компонентов данных и функций-компонентов класса. Такие компоненты называются статическими и имеют свойства, отличные от свойств не-статических компонентов.В случае не-статических компонентов для каждого объекта класса "существует" отдельная копия; в случае же статических компонентов существует только одна копия, а доступ к ней выполняется без ссылки на какой-либо конкретный объект класса. Если х это статический компонент класса Х, то к нему можно обратиться как Х::х (даже если объекты класса Х еще не созданы). Однако, можно выполнить доступ к х и при помощи обычных операций доступа к компонентам. Например, в виде y.x и yptr->x, гдеy это объект класса X, а yptr это указатель объекта класса X, хотя выражения y и yptr еще не вычислены. В частности, статическая функция-компонент может быть вызвана как с использованием специального синтаксиса вызова функций компонентов, так и без него.
class X (*
int member_int;
public:
static void func(int i, X* ptr);
*);
void g(void);
(*
X obj;
func(1, &obj); // ошибка, если где-нибудь еще
// не определена глобальная func()
X::func(1, &obj); // вызов static func() в X // допустим только для статических функций
obj.func(1, &obj); // то же самое (допустимо как для стати// ческих, так и не-статических функций)
*)
Поскольку статическая функция-компонент может вызываться без учета какого-либо конкретногообъекта, она не имеет указателя this. Следствие из этого таково, что статическая функция-компонент не имеет доступа к не-статическим компонентам без явного задания объекта при помощи .или ->. Например, сучетомобъявлений, сделанных впредыдущем примере, func может быть определена следующим образом:
void X%%func(int i, X* ptr)
(*
member_int = i; // на какой объект ссылается
// member_int? Ошибка !
ptr->member_int = 1; // так можно: теперь мы знаем! *)
Без учета встраиваемых функций,статические функции-компоненты глобальных классов имеют внешнийтип компоновки. Статические функции-компоненты не могут являться виртуальными функциями. Недопустимо иметь статическую и не-статическую функции-компоненты с одинаковыми именами и типами аргументов.
Объявление статического компонента данных в объявлении класса не является определением, поэтому где-нибудь еще должно иметься определение, отвечающее за распределение памяти и инициализацию. Определение статического компонентаданныхможет быть опущено, если действует средство "инициализации нулями по умолчанию".
Статические компоненты класса,объявленного локальным по отношению к некоторой функции, не имеют типа компоновки и не могут быть инициализированы. Статические компоненты глобального класса могут быть инициализированы подобно обычным глобальным объектам, но только вконтексте файла. Статические компоненты подчиняются обычным правилам доступа к компонентам класса, за исключением того, что они могут быть инициализированы.
class X (*
...
static int x;
...
*); int X::x = 1;
Главное использование статических компонентов состоит в том, чтобы отслеживать данные, общие для всех объектов класса,как например, число созданных объектов, либо использованный последним ресурс из пула, разделяемого всеми подобными объектами. Статические компоненты используются также для:
- уменьшения числа видимых глобальных имен
- того, чтобы сделать очевидным, какие именно статические объекты какому классу принадлежат
- разрешения управления доступам к их именам.
Контекст компонента
Выражение X::func() в примере, приведенномна стр.106 оригинала, используетимя класса Xс модификатором контекста доступа, обозначающим, что func, хотя и определена "вне" класса, в действительности является функцией-компонентом Х и существует в контексте Х. Влияние Х:: распространяется на тело определения этой функции. Это объясняет, почему возвращаемое функциейзначение i относится к X::i, char* i из Х, а не к глобальной переменной int i. Без модификатора Х:: функция func представляла бы обычную, не относящуюся к классу функцию, возвращающую глобальную переменную int i.
Следовательно, все функции-компоненты находятся в контексте своего класса, даже если они определены вне этого класса.
К компонентам данных класса Х можно обращаться при помощи операций выбора . и -> (как и в структурах С). Функциикомпоненты можновызывать также при помощи операций выбора (см. "Ключевое слово this на стр.105 оригинала). Например,
class X (*
public:
int i;
char name[20];
X *ptr1;
X *ptr2;
void Xfunc(char *data, X* left, X* right); // определение // находится не здесь
*);
void f(void);
(*
X x1, x2, *xptr=&x1; x1.i = 0; x2.i = x1.i; xptr->i = 1; x1.Xfunc("stan", &x2, xptr);
*)
Если m является компонентом или базовым компонентом класса Х, то выражение Х::m называется квалифицированным именем; оно имеет тот же тип, что и m, и является именующим выражением только в том случае, если именующим выражением является m. Ключевой момент здесьсостоит в том, что даже если имя класса Х скрыто другим именем, квалифицированное имя Х::m тем не менее обеспечит доступ к нужному имени класса, m.
Компоненты класса не могут быть добавлены к нему в другом разделе вашейпрограммы. Класс Х не может содержать объекты класса Х, но может содержать указатели или ссылки на объекты класса Х (отметим аналогию с типами структур и объединений С).
Управление доступом к компонентам
Компоненты класса получают атрибуты доступа либо по умолчанию (взависимости от ключакласса и местоположения объявления), либо при использовании какого-либо из спецификаторов доступа: public, private или protected. Значение этих атрибутов следующие:
public Компонент может быть использован любой функцией.
private Компонент может быть использован только функциями компонентами и "друзьями" класса, в котором он объявлен.
protected То же самое, что для private, но кроме того, компонент может быть использован функциями компонентов идрузьями классов, производных от объявленного класса,но только в объектах производного типа. (Производные классы рассматриваются в следующем разделе).
Спецификаторы доступа не влияют на объявления функций типа friend. (См. раздел "Друзья" классов" на стр.112 оригинала).