Класс, объявленный внутри другого класса, называется вложенным. Он является членом объемлющего класса, а его определение может находиться в любой из секций public, private или protected объемлющего класса.
Имя вложенного класса известно в области видимости объемлющего класса, но ни в каких других областях. Это означает, что оно не конфликтует с таким же именем, объявленным в объемлющей области видимости. Например:
class Node { /* ... */ }
class Tree {
public:
// Node инкапсулирован внутри области видимости класса Tree
// В этой области Tree::Node скрывает ::Node
class Node {...};
// правильно: разрешается в пользу вложенного класса: Tree::Node
Node *tree;
};
// Tree::Node невидима в глобальной области видимости
// Node разрешается в пользу глобального объявления Node
Node *pnode;
class List {
public:
// Node инкапсулирован внутри области видимости класса List
// В этой области List::Node скрывает ::Node
class Node {...};
// правильно: разрешается в пользу вложенного класса: List::Node
Node *list;
};
Для вложенного класса допустимы такие же виды членов, как и для невложенного:
// Не идеально, будем улучшать
class List {
public:
class ListItem {
friend class List; // объявление друга
ListItem( int val=0 ); // конструктор
ListItem *next; // указатель на собственный класс
int value;
};
// ...
private:
ListItem *list;
ListItem *at_end;
};
Закрытым называется член, который доступен только в определениях членов и друзей класса. У объемлющего класса нет права доступа к закрытым членам вложенного. Чтобы в определениях членов List можно было обращаться к закрытым членам ListItem, класс ListItem объявляет List как друга. Равно и вложенный класс не имеет никаких специальных прав доступа к закрытым членам объемлющего класса. Если бы нужно было разрешить ListItem доступ к закрытым членам класса List, то в объемлющем классе List следовало бы объявить вложенный класс как друга. В приведенном выше примере этого не сделано, поэтому ListItem не может обращаться к закрытым членам List.
Объявление ListItem открытым членом класса List означает, что вложенный класс можно использовать как тип во всей программе, в том числе и за пределами определений членов и друзей класса. Например:
// правильно: объявление в глобальной области видимости
List::ListItem *headptr;
Это дает более широкую область видимости, чем мы планировали. Вложенный ListItem поддерживает абстракцию класса List и не должен быть доступен во всей программе. Поэтому лучше объявить вложенный класс ListItem закрытым членом List:
// Не идеально, будем улучшать
class List {
public:
// ...
private:
class ListItem {
// ...
};
ListItem *list;
ListItem *at_end;
};
Теперь тип ListItem доступен только из определений членов и друзей класса List, поэтому все члены класса ListItem можно сделать открытыми. При таком подходе объявление List как друга ListItem становится ненужным. Вот новое определение класса List:
// так лучше
class List {
public:
// ...
private:
// Теперь ListItem закрытый вложенный тип
class ListItem {
// а его члены открыты
public:
ListItem( int val=0 );
ListItem *next;
int value;
};
ListItem *list;
ListItem *at_end;
};
Конструктор ListItem не задан как встроенный внутри определения класса и, следовательно, должен быть определен вне него. Но где именно? Конструктор класса ListItem не является членом List и, значит, не может быть определен в теле последнего; его нужно определить в глобальной области видимости – той, которая содержит определение объемлющего класса. Когда функция-член вложенного класса не определяется как встроенная в теле, она должна быть определена вне самого внешнего из объемлющих классов.
Вот как могло бы выглядеть определение конструктора ListItem. Однако показанный ниже синтаксис в глобальной области видимости некорректен:
class List {
public: