Вложенные классы
Объявление класса/структуры или объединения может содержать внутри другой класс. Это и будет Вложенный класс.
Объяснение
Имя вложенного класса существует в области видимости окружающего класса и поиск имён из метода вложенного класса посетит область видимости окружающего класса, если в своём ничего не найдёт. Как и любой элемент окружающего класса, вложенный класс имеет доступ ко всем именам (приватным, защищённым и так далее) к которым окружающий класс имеет доступ, но так или иначе является независимым и не имеет специального доступа к указателю this окружающего класса.
| Объявления во вложенном классе могут использовать только имена типов, статические элементы и перечислители из окружающего класса. | (до C++11) |
|
Объявления во вложенном классе могут использовать любые элементы окружающего класса, следуя обычным правилам использования для нестатических свойств. |
(начиная с C++11) |
int x,y; // глобальные переменные
class enclose // окружающий класс
{
// заметьте: закрытые элементы
int x;
static int s;
public:
struct inner // вложенный класс
{
void f(int i)
{
x = i; // Ошибка: не возможно записать в нестатический
// enclose::x без экземпляра
int a = sizeof x; // Ошибка до С++11,
// OK в С++11: операнд не оценивается
// такое использование нестатического
// enclose::x допустимо.
s = i; // OK: могу присвоить статическому enclose::s
::x = i; // OK: могу присвоить глобальному x
y = i; // OK: могу присвоить глобальному y
}
void g(enclose* p, int i)
{
p->x = i; // OK: присваивание enclose::x
}
};
};
Дружественные функции, определённые во вложенном классе не имеют специального доступа к элементам окружающего класса даже если поиск имён из тела методов, объявленных внутри вложенного класса, может найти закрытые элементы окружающего класа.
Внеклассовые определения элементов вложенного класса содержатся в пространстве имён внешнего класса:
struct enclose
{
struct inner
{
static int x;
void f(int i);
};
};
int enclose::inner::x = 1; // определение
void enclose::inner::f(int i) {} // определение
Вложенные классы могут быть заранее объявлены и позднее определены, как внутри тела внешнего класса, так и снаружи:
class enclose
{
class nested1; // предварительное объявление
class nested2; // предварительное объявление
class nested1 {}; // определение вложенного класса
};
class enclose::nested2 { }; // определение вложенного класса
Определения вложенного класса подчиняются спецификаторам доступа, закрытые элементы не могут быть именованы снаружи области видимости внешнего класса, несмотря на то что объекты этого класса могут быть использованы:
class enclose
{
struct nested // закрытый элемент
{
void g() {}
};
public:
static nested f() { return nested{}; }
};
int main()
{
//enclose::nested n1 = enclose::f(); // ошибка: 'nested' является закрытым
enclose::f().g(); // OK: не именует 'nested'
auto n2 = enclose::f(); // OK: не именует 'nested'
n2.g();
}
Отчёты об ошибках
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 45 | C++98 | элементы вложенных классов не могли иметь доступа к окружающему классу и его друзьям |
имеют те-же права доступа, как и другие элементы окружающего класса (также решило CWG проблемы №8 и №10) |
Ссылки
- C++20 стандарт (ISO/IEC 14882:2020):
- 11.4.10 Объявления вложенных классов [class.nest]
- C++17 стандарт (ISO/IEC 14882:2017):
- 12.2.5 Объявления вложенных классов [class.nest]
- C++14 стандарт (ISO/IEC 14882:2014):
- 9.7 Объявления вложенных классов [class.nest]
- C++11 стандарт (ISO/IEC 14882:2011):
- 9.7 Объявления вложенных классов [class.nest]
- C++98 стандарт (ISO/IEC 14882:1998):
- 9.7 Объявления вложенных классов [class.nest]