1. 项目的样式
2. 项目代码
头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSplitter>
#include <QStackedWidget>
#include <QDockWidget>
#include <QTextEdit>
#include <QListWidget>
#include <QTreeWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QGroupBox>
#include <QTabWidget>
#include <QTableWidget>
#include <QComboBox>
QT_BEGIN_NAMESPACE
namespace Ui
{
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
// StackedWidget相关槽函数
void switchToPage1();
void switchToPage2();
void switchToPage3();
void onComboBoxChanged(int index);
// Dock相关槽函数
void toggleLeftDock();
void toggleRightDock();
void toggleBottomDock();
private:
void setupUI(); // 初始化界面
void setupSplitter(); // 设置分割器
void setupStackedWidget(); // 设置堆叠控件
void setupDockWidgets(); // 设置停靠控件
void setupMenuAndToolbar(); // 设置菜单和工具栏
Ui::MainWindow *ui;
// Splitter相关成员
QSplitter *mainSplitter; // 主分割器(垂直)
QSplitter *topSplitter; // 顶部分割器(水平)
QSplitter *bottomSplitter; // 底部分割器(水平)
// StackedWidget相关成员
QStackedWidget *stackedWidget; // 堆叠控件
QWidget *page1; // 页面1:文本编辑器
QWidget *page2; // 页面2:表格控件
QWidget *page3; // 页面3:树形控件
QComboBox *pageSelector; // 页面选择器
// 各页面的控件
QTextEdit *textEdit; // 文本编辑器
QTableWidget *tableWidget; // 表格控件
QTreeWidget *treeWidget; // 树形控件
// Dock相关成员
QDockWidget *leftDock; // 左侧停靠窗口
QDockWidget *rightDock; // 右侧停靠窗口
QDockWidget *bottomDock; // 底部停靠窗口
// Dock内容控件
QListWidget *projectList; // 项目列表
QTextEdit *propertyEditor; // 属性编辑器
QTextEdit *outputConsole; // 输出控制台
// 控制按钮
QPushButton *page1Btn;
QPushButton *page2Btn;
QPushButton *page3Btn;
};
#endif // MAINWINDOW_H
cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QApplication>
#include <QHeaderView>
#include <QTreeWidgetItem>
#include <QTableWidgetItem>
#include <QMenuBar>
#include <QToolBar>
#include <QAction>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow), mainSplitter(nullptr), topSplitter(nullptr), bottomSplitter(nullptr), stackedWidget(nullptr), page1(nullptr), page2(nullptr), page3(nullptr), pageSelector(nullptr), textEdit(nullptr), tableWidget(nullptr), treeWidget(nullptr), leftDock(nullptr), rightDock(nullptr), bottomDock(nullptr), projectList(nullptr), propertyEditor(nullptr), outputConsole(nullptr), page1Btn(nullptr), page2Btn(nullptr), page3Btn(nullptr)
{
ui->setupUi(this);
// 设置窗口标题和大小
setWindowTitle("Qt Layout Demo - Splitter, Dock, Stacked");
resize(1200, 800);
// 初始化界面
setupUI();
setupSplitter();
setupStackedWidget();
setupDockWidgets();
setupMenuAndToolbar();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setupUI()
{
/**
* 设置基本UI结构
* 使用QSplitter创建可调节大小的分割区域
*/
// 创建主分割器(垂直分割)
mainSplitter = new QSplitter(Qt::Vertical, this);
setCentralWidget(mainSplitter);
// 创建顶部分割器(水平分割)
topSplitter = new QSplitter(Qt::Horizontal);
// 创建底部分割器(水平分割)
bottomSplitter = new QSplitter(Qt::Horizontal);
// 将分割器添加到主分割器中
mainSplitter->addWidget(topSplitter);
mainSplitter->addWidget(bottomSplitter);
// 设置分割器比例 (上下比例为 3:1)
mainSplitter->setSizes({600, 200});
mainSplitter->setStretchFactor(0, 3);
mainSplitter->setStretchFactor(1, 1);
}
void MainWindow::setupSplitter()
{
/**
* 设置分割器的详细配置
* QSplitter允许用户通过拖拽来调整子控件的大小
*/
// 配置主分割器
mainSplitter->setHandleWidth(3); // 设置分割线宽度
mainSplitter->setChildrenCollapsible(false); // 禁止子控件折叠
// 配置顶部分割器
topSplitter->setHandleWidth(3);
topSplitter->setChildrenCollapsible(false);
// 配置底部分割器
bottomSplitter->setHandleWidth(3);
bottomSplitter->setChildrenCollapsible(false);
}
void MainWindow::setupStackedWidget()
{
/**
* 设置堆叠控件
* QStackedWidget用于在同一位置显示多个页面,一次只显示一个页面
*/
// 创建堆叠控件
stackedWidget = new QStackedWidget();
// 创建控制面板
QWidget *controlPanel = new QWidget();
QVBoxLayout *controlLayout = new QVBoxLayout(controlPanel);
// 创建页面选择器
pageSelector = new QComboBox();
pageSelector->addItem("页面1 - 文本编辑器");
pageSelector->addItem("页面2 - 表格控件");
pageSelector->addItem("页面3 - 树形控件");
// 创建按钮控制
QGroupBox *buttonGroup = new QGroupBox("页面切换按钮");
QHBoxLayout *buttonLayout = new QHBoxLayout(buttonGroup);
page1Btn = new QPushButton("文本编辑器");
page2Btn = new QPushButton("表格控件");
page3Btn = new QPushButton("树形控件");
page1Btn->setCheckable(true);
page2Btn->setCheckable(true);
page3Btn->setCheckable(true);
page1Btn->setChecked(true); // 默认选中第一页
buttonLayout->addWidget(page1Btn);
buttonLayout->addWidget(page2Btn);
buttonLayout->addWidget(page3Btn);
// 添加到控制面板
controlLayout->addWidget(new QLabel("使用下拉框选择页面:"));
controlLayout->addWidget(pageSelector);
controlLayout->addWidget(buttonGroup);
controlLayout->addStretch();
// 创建页面1:文本编辑器
page1 = new QWidget();
QVBoxLayout *page1Layout = new QVBoxLayout(page1);
page1Layout->addWidget(new QLabel("页面1 - 文本编辑器"));
textEdit = new QTextEdit();
textEdit->setPlainText("这是一个文本编辑器示例。\n\n"
"QStackedWidget特点:\n"
"1. 可以在同一位置显示多个页面\n"
"2. 一次只显示一个页面\n"
"3. 适用于向导、选项卡等场景\n"
"4. 支持动画切换效果\n\n"
"您可以在这里编辑文本...");
page1Layout->addWidget(textEdit);
// 创建页面2:表格控件
page2 = new QWidget();
QVBoxLayout *page2Layout = new QVBoxLayout(page2);
page2Layout->addWidget(new QLabel("页面2 - 表格控件"));
tableWidget = new QTableWidget(5, 4);
QStringList headers = {"姓名", "年龄", "部门", "工资"};
tableWidget->setHorizontalHeaderLabels(headers);
// 填充示例数据
QString data[5][4] = {
{"张三", "25", "开发部", "8000"},
{"李四", "30", "测试部", "7500"},
{"王五", "28", "设计部", "7000"},
{"赵六", "32", "产品部", "9000"},
{"钱七", "26", "运营部", "6500"}};
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 4; j++)
{
tableWidget->setItem(i, j, new QTableWidgetItem(data[i][j]));
}
}
tableWidget->horizontalHeader()->setStretchLastSection(true);
page2Layout->addWidget(tableWidget);
// 创建页面3:树形控件
page3 = new QWidget();
QVBoxLayout *page3Layout = new QVBoxLayout(page3);
page3Layout->addWidget(new QLabel("页面3 - 树形控件"));
treeWidget = new QTreeWidget();
treeWidget->setHeaderLabels({"项目结构", "类型", "大小"});
// 创建树形结构
QTreeWidgetItem *root = new QTreeWidgetItem(treeWidget, {"项目根目录", "文件夹", ""});
QTreeWidgetItem *src = new QTreeWidgetItem(root, {"src", "文件夹", ""});
new QTreeWidgetItem(src, {"main.cpp", "C++文件", "2.1KB"});
new QTreeWidgetItem(src, {"mainwindow.cpp", "C++文件", "8.5KB"});
new QTreeWidgetItem(src, {"mainwindow.h", "头文件", "1.2KB"});
QTreeWidgetItem *resources = new QTreeWidgetItem(root, {"resources", "文件夹", ""});
new QTreeWidgetItem(resources, {"icons", "文件夹", ""});
new QTreeWidgetItem(resources, {"images", "文件夹", ""});
QTreeWidgetItem *build = new QTreeWidgetItem(root, {"build", "文件夹", ""});
new QTreeWidgetItem(build, {"debug", "文件夹", ""});
new QTreeWidgetItem(build, {"release", "文件夹", ""});
treeWidget->expandAll();
page3Layout->addWidget(treeWidget);
// 将页面添加到堆叠控件
stackedWidget->addWidget(page1);
stackedWidget->addWidget(page2);
stackedWidget->addWidget(page3);
// 设置默认页面
stackedWidget->setCurrentIndex(0);
// 将控制面板和堆叠控件添加到顶部分割器
topSplitter->addWidget(controlPanel);
topSplitter->addWidget(stackedWidget);
// 设置比例 (控制面板:主内容 = 1:4)
topSplitter->setSizes({200, 800});
// 连接信号和槽
connect(pageSelector, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::onComboBoxChanged);
connect(page1Btn, &QPushButton::clicked, this, &MainWindow::switchToPage1);
connect(page2Btn, &QPushButton::clicked, this, &MainWindow::switchToPage2);
connect(page3Btn, &QPushButton::clicked, this, &MainWindow::switchToPage3);
}
void MainWindow::setupDockWidgets()
{
/**
* 设置停靠控件
* QDockWidget可以停靠在主窗口的四周,用户可以拖拽移动或关闭
*/
// 创建左侧停靠窗口 - 项目浏览器
leftDock = new QDockWidget("项目浏览器", this);
projectList = new QListWidget();
// 添加项目列表项
QStringList projects = {
"项目1 - Qt应用程序",
"项目2 - Web前端",
"项目3 - 移动应用",
"项目4 - 数据分析",
"项目5 - 机器学习",
"项目6 - 游戏开发"};
projectList->addItems(projects);
leftDock->setWidget(projectList);
// 设置停靠区域和特性
leftDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
leftDock->setFeatures(QDockWidget::DockWidgetMovable |
QDockWidget::DockWidgetClosable |
QDockWidget::DockWidgetFloatable);
// 创建右侧停靠窗口 - 属性编辑器
rightDock = new QDockWidget("属性编辑器", this);
propertyEditor = new QTextEdit();
propertyEditor->setPlainText("属性编辑器\n\n"
"在这里可以编辑选中对象的属性:\n\n"
"对象名称: MainWindow\n"
"类型: QMainWindow\n"
"宽度: 1200\n"
"高度: 800\n"
"可见: true\n"
"启用: true\n\n"
"QDockWidget特点:\n"
"1. 可以停靠在窗口四周\n"
"2. 支持拖拽移动\n"
"3. 可以浮动显示\n"
"4. 可以隐藏/显示");
rightDock->setWidget(propertyEditor);
rightDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
rightDock->setFeatures(QDockWidget::DockWidgetMovable |
QDockWidget::DockWidgetClosable |
QDockWidget::DockWidgetFloatable);
// 创建底部停靠窗口 - 输出控制台
bottomDock = new QDockWidget("输出控制台", this);
outputConsole = new QTextEdit();
outputConsole->setPlainText("输出控制台\n"
"======================\n"
"[INFO] 应用程序启动成功\n"
"[INFO] 界面组件初始化完成\n"
"[INFO] Splitter配置完成\n"
"[INFO] StackedWidget配置完成\n"
"[INFO] DockWidget配置完成\n"
"[DEBUG] 当前页面: 文本编辑器\n"
"[INFO] 准备就绪,等待用户操作...\n");
bottomDock->setWidget(outputConsole);
bottomDock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea);
bottomDock->setFeatures(QDockWidget::DockWidgetMovable |
QDockWidget::DockWidgetClosable |
QDockWidget::DockWidgetFloatable);
// 添加停靠窗口到主窗口
addDockWidget(Qt::LeftDockWidgetArea, leftDock);
addDockWidget(Qt::RightDockWidgetArea, rightDock);
addDockWidget(Qt::BottomDockWidgetArea, bottomDock);
// 设置停靠窗口的大小
resizeDocks({leftDock}, {200}, Qt::Horizontal);
resizeDocks({rightDock}, {250}, Qt::Horizontal);
resizeDocks({bottomDock}, {150}, Qt::Vertical);
// 创建一个简单的状态显示控件添加到底部分割器
QTextEdit *statusDisplay = new QTextEdit();
statusDisplay->setPlainText("状态显示区域\n\n"
"这里显示应用程序的运行状态信息。\n\n"
"分割器使用说明:\n"
"- 可以通过拖拽分割线调整区域大小\n"
"- 支持嵌套分割(水平和垂直)\n"
"- 可以设置最小/最大尺寸\n"
"- 支持比例分配");
statusDisplay->setMaximumHeight(200);
bottomSplitter->addWidget(statusDisplay);
}
void MainWindow::setupMenuAndToolbar()
{
/**
* 设置菜单栏和工具栏
*/
// 创建菜单
QMenu *viewMenu = menuBar()->addMenu("视图(&V)");
QMenu *windowMenu = menuBar()->addMenu("窗口(&W)");
// 添加停靠窗口切换动作
QAction *toggleLeftAction = new QAction("切换项目浏览器", this);
QAction *toggleRightAction = new QAction("切换属性编辑器", this);
QAction *toggleBottomAction = new QAction("切换输出控制台", this);
toggleLeftAction->setCheckable(true);
toggleRightAction->setCheckable(true);
toggleBottomAction->setCheckable(true);
toggleLeftAction->setChecked(true);
toggleRightAction->setChecked(true);
toggleBottomAction->setChecked(true);
viewMenu->addAction(toggleLeftAction);
viewMenu->addAction(toggleRightAction);
viewMenu->addAction(toggleBottomAction);
// 连接菜单动作
connect(toggleLeftAction, &QAction::triggered, this, &MainWindow::toggleLeftDock);
connect(toggleRightAction, &QAction::triggered, this, &MainWindow::toggleRightDock);
connect(toggleBottomAction, &QAction::triggered, this, &MainWindow::toggleBottomDock);
// 添加窗口操作菜单
QAction *aboutAction = new QAction("关于", this);
windowMenu->addAction(aboutAction);
connect(aboutAction, &QAction::triggered, [this]()
{ QMessageBox::about(this, "关于",
"Qt Layout Demo\n\n"
"这个示例展示了Qt中三种重要的布局控件:\n\n"
"1. QSplitter - 分割器控件\n"
"2. QDockWidget - 停靠控件\n"
"3. QStackedWidget - 堆叠控件\n\n"
"通过这些控件的组合使用,可以创建出功能丰富、\n"
"用户体验良好的桌面应用程序界面。"); });
// 创建工具栏
QToolBar *mainToolBar = addToolBar("主工具栏");
mainToolBar->addAction("新建");
mainToolBar->addAction("打开");
mainToolBar->addAction("保存");
mainToolBar->addSeparator();
mainToolBar->addAction("运行");
mainToolBar->addAction("调试");
}
// StackedWidget相关槽函数实现
void MainWindow::switchToPage1()
{
stackedWidget->setCurrentIndex(0);
pageSelector->setCurrentIndex(0);
// 更新按钮状态
page1Btn->setChecked(true);
page2Btn->setChecked(false);
page3Btn->setChecked(false);
// 更新输出控制台
outputConsole->append("[INFO] 切换到页面1 - 文本编辑器");
}
void MainWindow::switchToPage2()
{
stackedWidget->setCurrentIndex(1);
pageSelector->setCurrentIndex(1);
// 更新按钮状态
page1Btn->setChecked(false);
page2Btn->setChecked(true);
page3Btn->setChecked(false);
// 更新输出控制台
outputConsole->append("[INFO] 切换到页面2 - 表格控件");
}
void MainWindow::switchToPage3()
{
stackedWidget->setCurrentIndex(2);
pageSelector->setCurrentIndex(2);
// 更新按钮状态
page1Btn->setChecked(false);
page2Btn->setChecked(false);
page3Btn->setChecked(true);
// 更新输出控制台
outputConsole->append("[INFO] 切换到页面3 - 树形控件");
}
void MainWindow::onComboBoxChanged(int index)
{
stackedWidget->setCurrentIndex(index);
// 更新按钮状态
page1Btn->setChecked(index == 0);
page2Btn->setChecked(index == 1);
page3Btn->setChecked(index == 2);
// 更新输出控制台
QString pages[] = {"文本编辑器", "表格控件", "树形控件"};
outputConsole->append(QString("[INFO] 通过下拉框切换到页面%1 - %2").arg(index + 1).arg(pages[index]));
}
// Dock相关槽函数实现
void MainWindow::toggleLeftDock()
{
if (leftDock->isVisible())
{
leftDock->hide();
outputConsole->append("[INFO] 隐藏项目浏览器");
}
else
{
leftDock->show();
outputConsole->append("[INFO] 显示项目浏览器");
}
}
void MainWindow::toggleRightDock()
{
if (rightDock->isVisible())
{
rightDock->hide();
outputConsole->append("[INFO] 隐藏属性编辑器");
}
else
{
rightDock->show();
outputConsole->append("[INFO] 显示属性编辑器");
}
}
void MainWindow::toggleBottomDock()
{
if (bottomDock->isVisible())
{
bottomDock->hide();
}
else
{
bottomDock->show();
outputConsole->append("[INFO] 切换输出控制台显示状态");
}
}
3. 代码调用文字流程图
Qt Layout Demo 程序流程图
┌─────────────────────────────────────────────────────────────────┐
│ 程序启动流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ main.cpp │
│ │ │
│ ▼ │
│ QApplication app(argc, argv) │
│ │ │
│ ▼ │
│ MainWindow window │
│ │ │
│ ▼ │
│ MainWindow构造函数 │
│ │ │
│ ├─────────────────────────────────────────┐ │
│ │ ui->setupUi(this) │ │
│ │ setWindowTitle() │ │
│ │ resize(1200, 800) │ │
│ │ setupUI() │ │
│ │ setupSplitter() │ 详细初始化流程 │
│ │ setupStackedWidget() │ │
│ │ setupDockWidgets() │ │
│ │ setupMenuAndToolbar() │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ window.show() │
│ │ │
│ ▼ │
│ app.exec() │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 1. setupUI()流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ mainSplitter = new QSplitter(Qt::Vertical, this) │
│ │ │
│ ▼ │
│ setCentralWidget(mainSplitter) │
│ │ │
│ ▼ │
│ topSplitter = new QSplitter(Qt::Horizontal) │
│ │ │
│ ▼ │
│ bottomSplitter = new QSplitter(Qt::Horizontal) │
│ │ │
│ ▼ │
│ mainSplitter->addWidget(topSplitter) │
│ mainSplitter->addWidget(bottomSplitter) │
│ │ │
│ ▼ │
│ mainSplitter->setSizes({600,200}) │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 2. setupSplitter()流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ mainSplitter->setHandleWidth(3) │
│ mainSplitter->setChildrenCollapsible(false) │
│ │ │
│ ▼ │
│ topSplitter->setHandleWidth(3) │
│ topSplitter->setChildrenCollapsible(false) │
│ │ │
│ ▼ │
│ bottomSplitter->setHandleWidth(3) │
│ bottomSplitter->setChildrenCollapsible(false) │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 3. setupStackedWidget()流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 创建堆叠控件: stackedWidget = new QStackedWidget │
│ │ │
│ ▼ │
│ 创建控制面板: │
│ controlPanel = new QWidget │
│ pageSelector = new QComboBox │
│ 按钮组 (page1Btn, page2Btn, page3Btn) │
│ │ │
│ ▼ │
│ 创建页面内容: │
│ page1 (文本编辑器) page2 (表格控件) page3 (树形控件) │
│ │ │
│ ▼ │
│ 添加页面到堆叠控件 │
│ │ │
│ ▼ │
│ 添加到分割器: │
│ topSplitter->addWidget(controlPanel) │
│ topSplitter->addWidget(stackedWidget) │
│ │ │
│ ▼ │
│ 连接信号和槽 │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 4. setupDockWidgets()流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 创建左侧停靠窗口 │
│ 创建右侧停靠窗口 │
│ 创建底部停靠窗口 │
│ │ │
│ ▼ │
│ 添加停靠窗口到主窗口 │
│ addDockWidget(Qt::LeftDockWidgetArea, leftDock) │
│ addDockWidget(Qt::RightDockWidgetArea, rightDock) │
│ addDockWidget(Qt::BottomDockWidgetArea, bottomDock) │
│ │ │
│ ▼ │
│ 设置停靠窗口大小 │
│ │ │
│ ▼ │
│ 创建状态显示控件 │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 5. setupMenuAndToolbar()流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 创建菜单: 视图(&V) 窗口(&W) │
│ │ │
│ ▼ │
│ 添加停靠窗口切换动作 │
│ │ │
│ ▼ │
│ 连接菜单动作到槽函数 │
│ │ │
│ ▼ │
│ 添加关于菜单 │
│ │ │
│ ▼ │
│ 创建工具栏: 主工具栏 │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 用户交互流程 │
├─────────────────────────────────────────────────────────────────┤
│ 页面切换操作: │
│ 用户点击页面按钮 或 选择下拉框选项 │
│ │ │
│ ▼ │
│ 信号发出: clicked() 或 currentIndexChanged │
│ │ │
│ ▼ │
│ 槽函数调用: switchToPage1()/switchToPage2()/switchToPage3() │
│ │ │
│ ▼ │
│ 执行页面切换 │
│ │
│ 停靠窗口切换操作: │
│ 用户点击菜单项 │
│ │ │
│ ▼ │
│ 信号发出: triggered() │
│ │ │
│ ▼ │
│ 槽函数调用: toggleLeftDock()/toggleRightDock()/toggleBottomDock()│
│ │ │
│ ▼ │
│ 执行切换操作 │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 内存管理流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ MainWindow析构函数 │
│ ~MainWindow() → delete ui │
│ │ │
│ ▼ │
│ Qt父子关系自动管理其他控件内存 │
│ (所有new出来的控件都通过父子关系自动管理,无需手动delete) │
│ │
└─────────────────────────────────────────────────────────────────┘