Выбрать главу

class ListItem {

// pref имеет тип List::Ref*

Ref *pref;

};

};

Если бы ListItem не был объявлен перед определением класса Ref, то объявление члена pli было бы ошибкой.

Вложенный класс не может напрямую обращаться к нестатическим членам объемлющего, даже если они открыты. Любое такое обращение должно производиться через указатель, ссылку или объект объемлющего класса. Например:

class List {

public:

int init( int );

private:

class List::ListItem {

public:

ListItem( int val=0 );

void mf( const List & );

int value;

};

};

List::ListItem::ListItem { int val )

{

// List::init() - нестатический член класса List

// должен использоваться через объект или указатель на тип List

value = init( val ); // ошибка: неверное использование init

};

При использовании нестатических членов класса компилятор должен иметь возможность идентифицировать объект, которому принадлежит такой член. Внутри функции-члена класса ListItem указатель this неявно применяется лишь к его членам. Благодаря неявному this мы знаем, что член value относится к объекту, для которого вызван конструктор. Внутри конструктора ListItem указатель this имеет тип ListItem*. Для доступа же к функции-члену init() нужен объект типа List или указатель типа List*.

Следующая функция-член mf() обращается к init() с помощью параметра-ссылки. Таким образом, init() вызывается для объекта, переданного в аргументе функции:

void List::ListItem::mf( List &i1 ) {

memb = i1.init(); // правильно: обращается к init() по ссылке

}

Хотя для доступа к нестатическим членам объемлющего класса нужен объект, указатель или ссылка, к статическим его членам, именам типов и элементам перечисления вложенный класс может обращаться напрямую (если, конечно, эти члены открыты). Имя типа – это либо имя typedef, либо имя перечисления, либо имя класса. Например:

class List {

public:

typedef int (*pFunc)();

enum ListStatus { Good, Empty, Corrupted };

//...

private:

class ListItem {

public:

void check_status();

ListStatus status; // правильно

pFunc action; // правильно

// ...

};

// ...

};

pFunc, ListStatus и ListItem – все это вложенные имена типов в области видимости объемлющего класса List. К ним, а также к элементам перечисления ListStatus можно обращаться в области видимости класса ListItem даже без квалификации:

void List::ListItem::check_status()

{

ListStatus s = status;

switch ( s ) {

case Empty: ...

case Corrupted: ...

case Good: ...

}

}

Вне области видимости ListItem и List при обращении к статическим членам, именам типов и элементам перечисления объемлющего класса требуется оператор разрешения области видимости:

List::pFunc myAction; // правильно

List::ListStatus stat = List::Empty; // правильно

При обращении к элементам перечисления мы не пишем:

List::ListStatus::Empty

поскольку они доступны непосредственно в той области видимости, в которой определено само перечисление. Почему? Потому что с ним, в отличие от класса, не связана отдельная область.

13.10.1. Разрешение имен в области видимости вложенного класса

Посмотрим, как разрешаются имена в определениях вложенного класса и его членов.

Имя, встречающееся в определении вложенного класса (кроме тех, которые употребляются во встроенных функциях-членах и аргументах по умолчанию) разрешается следующим образом:

* Просматриваются члены вложенного класса, расположенные перед употреблением имени.

* Если шаг 1 не привел к успеху, то просматриваются объявления членов объемлющего класса, расположенные перед употреблением имени. Если и этого недостаточно, то просматриваются объявления, расположенные в области видимости пространства имен перед определением вложенного класса.

Например:

enum ListStatus { Good, Empty, Corrupted };

class List {

public:

// ...

private:

class ListItem {

public:

// Смотрим в:

// 1) List::ListItem

// 2) List

// 3) глобальной области видимости

ListStatus status; // относится к глобальному перечислению

// ...

};

// ...

};

Сначала компилятор ищет объявление ListStatus в области видимости класса ListItem. Поскольку его там нет, поиск продолжается в области видимости List, а затем в глобальной. При этом во всех трех областях просматриваются только объявления, предшествующие использованию ListStatus. В конце концов находится глобальное объявление перечисления ListStatus – оно и будет типом, использованным в объявлении status.