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".

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

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

LinkedList::Node node(42);    // Только если Node является публичным

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

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

challenge icon

Задание

Легко

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

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

#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: Создать проект с заданным именем
    
    // TODO: Добавить обе задачи в проект
    
    // TODO: Отобразить начальный статус
    
    // TODO: Завершить первую задачу
    
    // TODO: Снова отобразить статус, чтобы показать изменения
    
    return 0;
}
quiz iconПроверьте себя

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

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