Вложенные классы
Объявление класса/структуры или объединения может содержать внутри другой класс. Это и будет Вложенный класс.
[править] Объяснение
Имя вложенного класса существует в области видимости окружающего класса и поиск имён из метода вложенного класса посетит область видимости окружающего класса, если в своём ничего не найдёт. Как и любой элемент окружающего класса, вложенный класс имеет доступ ко всем именам (приватным, защищённым и так далее) к которым окружающий класс имеет доступ, но так или иначе является независимым и не имеет специального доступа к указателю 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]