Вложенные и внутренние классы
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 38 из 104.
Вложенный класс — это класс, определенный внутри другого класса. Этот метод помогает организовать код, группируя связанные типы вместе, и может улучшить инкапсуляцию, скрывая детали реализации.
class LinkedList {
public:
class Node { // Вложенный класс
int data;
Node* next;
public:
Node(int d) : data(d), next(nullptr) {}
friend class LinkedList;
};
private:
Node* head;
public:
LinkedList() : head(nullptr) {}
};Вложенный класс Node тесно связан с LinkedList — он имеет смысл только в этом контексте. Вкладывая его, мы обозначаем эту связь и избегаем загрязнения глобального пространства имен общим именем, таким как «Node».
Вложенные классы следуют тем же правилам доступа, которые вы изучили. Вложенный класс, объявленный в секции private, доступен только внутри внешнего класса.
При объявлении public внешний код может использовать его с помощью оператора области видимости:
LinkedList::Node node(42); // Только если Node является публичнымОдна важная деталь: вложенный класс не получает автоматически доступ к приватным членам внешнего класса. Это отдельные классы, которые просто разделяют общее пространство имен.
Если вложенному классу требуется доступ, вы должны объявить его как friend, как показано в примере выше.
Задание
ЛегкоДавайте создадим простую систему управления задачами, в которой задачи организованы внутри проекта. Это идеальный сценарий для использования вложенных классов — Task имеет смысл только в контексте Project, поэтому мы вложим его внутрь, чтобы сохранить структуру кода и избежать загрязнения глобального пространства имен.
Вы создадите два файла для организации вашего кода:
Project.h: Определите классProject, который содержит вложенный классTask.Ваш вложенный класс
Taskдолжен быть объявлен в секции public классаProject, чтобы к нему можно было обращаться извне. Он должен хранить название задачи (string) и статус завершения (bool). Создайте конструктор, который принимает название и по умолчанию помечает задачу как незавершенную. Добавьте методыcomplete(), чтобы пометить её как выполненную,isComplete()для проверки статуса иgetName()для получения названия.Сам класс
Projectдолжен хранить название проекта (string) и коллекцию указателей наTask. Предусмотрите конструктор, принимающий название проекта, методaddTask(const std::string& taskName), который создает новую задачу и добавляет её в проект, методcompleteTask(const std::string& taskName), который находит и завершает задачу по названию, и методdisplayStatus(), который выводит название проекта, а затем статус каждой задачи.Поскольку
Taskвложен вProject, классProjectможет работать с задачами напрямую. Не забудьте освободить динамически выделенную память для задач в деструкторе.main.cpp: Объедините всё вместе, считав название проекта и названия двух задач из входных данных (всего три строки). СоздайтеProjectс заданным именем, добавьте в него обе задачи, а затем выведите начальный статус. Выполните первую задачу и снова выведите статус, чтобы показать изменения.Формат вывода для
displayStatus()должен быть следующим:Project: <project_name> - <task_name>: [incomplete] - <task_name>: [complete]Используйте
[complete]или[incomplete]в зависимости от статуса каждой задачи.
Эта задача демонстрирует, как вложенные классы помогают организовать связанные типы — класс Task тесно связан с Project и не имел бы смысла сам по себе. Вкладывая его, мы четко обозначаем эту взаимосвязь в структуре нашего кода.
Шпаргалка
Вложенный класс — это класс, определенный внутри другого класса. Он помогает организовать код, группируя связанные типы вместе, и улучшает инкапсуляцию.
class LinkedList {
public:
class Node { // Вложенный класс
int data;
Node* next;
public:
Node(int d) : data(d), next(nullptr) {}
friend class LinkedList;
};
private:
Node* head;
public:
LinkedList() : head(nullptr) {}
};Вложенные классы следуют стандартным правилам доступа. Вложенный класс в приватной секции доступен только внутри внешнего класса. Если он объявлен как public, внешний код может использовать его с помощью оператора разрешения области видимости:
LinkedList::Node node(42); // Только если Node является публичнымВложенный класс не получает автоматического доступа к приватным членам внешнего класса. Если доступ необходим, объявите его дружественным (friend):
friend class LinkedList;Попробуйте сами
#include <iostream>
#include <string>
#include "Project.h"
using namespace std;
int main() {
// Чтение входных данных
string projectName;
string task1Name;
string task2Name;
getline(cin, projectName);
getline(cin, task1Name);
getline(cin, task2Name);
// TODO: Создать Project с заданным именем
// TODO: Добавить обе задачи в проект
// TODO: Отобразить начальный статус
// TODO: Завершить первую задачу
// TODO: Снова отобразить статус, чтобы показать изменения
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнер