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 является публичным

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

Если вложенному классу требуется доступ, вы должны объявить его как friend, как показано в примере выше.

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

Шпаргалка

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

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

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

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