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.