Menu
CoddyTech

嵌套类与内部类

本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 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 是 public 时

关于访问方向,有一个重要细节:嵌套类是外部类的成员,拥有与其他成员相同的访问权限,因此无需友元声明就能访问外部类的私有成员。不过反过来并不会自动成立:外部类无法访问嵌套类的私有成员。

这就是为什么上面的示例中 Node 声明了 friend class LinkedList;:friend 声明允许外围的 LinkedList 访问 Node 的私有成员,这是不会自动授予的访问权限。

challenge icon

挑战

简单

让我们构建一个简单的任务管理系统,在项目中组织任务。这正是使用嵌套类的理想场景:Task 只有在 Project 的上下文中才有意义,因此我们会将它嵌套在其中,让代码更有条理,并避免污染全局命名空间。

你将创建两个文件来组织代码:

  • Project.h:定义一个包含嵌套 Task 类的 Project 类。

    你的嵌套 Task 类应声明在 Project 的 public 部分,这样才能从外部访问它。它应存储任务名称(字符串)和完成状态(布尔值)。为它添加一个构造函数,接收名称,并默认将任务设为未完成。添加 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++ 在线编译器