嵌套类与内部类
本课属于 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 的私有成员,这是不会自动授予的访问权限。
挑战
简单让我们构建一个简单的任务管理系统,在项目中组织任务。这正是使用嵌套类的理想场景: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;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C++ 在线编译器