Menu
Coddy logo textTech

Вложенные и внутренние классы

Часть раздела Объектно-ориентированное программирование путешествия по 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".

Вложенные классы подчиняются тем же правилам доступа, которые вы изучили. Вложенный класс, объявленный в закрытой секции, доступен только внутри внешнего класса.

При объявлении как public внешний код может использовать его с оператором области видимости:

LinkedList::Node node(42);    // Только если Node является public

Одна важная деталь, касающаяся направления: вложенный class является членом охватывающего class и имеет те же права доступа, что и любой другой член, поэтому он может обращаться к закрытым членам охватывающего class без объявления friend. Однако обратное не происходит автоматически: охватывающий class не имеет доступа к закрытым членам вложенного class.

Вот почему Node объявляет friend class LinkedList; в приведённом выше примере: объявление friend предоставляет охватывающему его LinkedList доступ к закрытым членам Node — это единственное направление, для которого доступ не предоставляется автоматически.

challenge icon

Задание

Легко

Давайте создадим простую систему управления задачами, в которой задачи организованы внутри проекта. Это идеальный сценарий для вложенных классов: Task имеет смысл только в контексте Project, поэтому мы вложим его внутрь, чтобы сохранить организованность кода и не засорять глобальное пространство имён.

Вы создадите два файла, чтобы организовать код:

  • Project.h: определите класс Project, содержащий вложенный класс Task.

    Ваш вложенный класс Task должен быть объявлен в секции public класса Project, чтобы к нему можно было обращаться извне. Он должен хранить имя задачи (строка) и статус выполнения (bool). Добавьте ему конструктор, который принимает имя и по умолчанию устанавливает для задачи статус incomplete. Включите методы complete(), чтобы отметить задачу как выполненную, isComplete(), чтобы проверить её статус, и getName(), чтобы получить имя.

    Сам класс Project должен хранить имя проекта (строка) и коллекцию указателей на 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 и сам по себе не имел бы смысла. Вложив его внутрь, мы ясно обозначаем эту связь в структуре нашего кода.

Попробуйте сами

#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;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++