一、背景
c
class TestTreeData: public data::TreeObjectData
{
public:
TestTreeData();
~TestTreeData();
protected:
void InitData()override;
private:
class Internal;
std::unique_ptr<Internal> mTreeList;
};
class TestTreeData::Internal
{
public:
Internal() {
}
~Internal() {
}
//求解设定
Ptr<TestSolutionControlTreeData> SolutionControlTreeData;
};
二、原因
Internal 是Pimpl设计模式 的实现,用于存放 TestTreeData 的所有私有数据,将内部实现与对外接口分离,让外部类保持简洁干净。
逐部分解析
1. class Internal;
-
仅为前向声明
-
作用:告诉编译器存在这个类,具体定义后续提供
2. std::unique_ptr<Internal> mTreeList;
-
管理内部对象的智能指针
-
是
TestTreeData唯一的私有成员,结构极简
3. TestTreeData::Internal 实现类
-
真正存放内部数据的容器
-
当前存储:
SolutionControlTreeData(求解设定树节点数据) -
所有私有成员变量、内部状态都放在这里
这种写法的3个核心优势
- 头文件干净整洁
外部类看不到内部私有成员,接口清晰
- 降低代码耦合
内部依赖的类型不暴露给外部,加快编译速度
- 易于扩展维护
新增成员变量只需修改 Internal,不影响外部类结构
直观对比
不使用 Internal(结构混乱)
C++
class TestTreeData: public TreeObjectData
{
private:
Ptr<TestSolutionControlTreeData> SolutionControlTreeData;
// 后续会不断新增私有成员...
};
使用 Internal(结构清爽)
C++
class TestTreeData: public TreeObjectData
{
private:
class Internal;
std::unique_ptr<Internal> mTreeList;
};
业务层面作用
TestTreeData 作为树数据节点,需要持有求解设定相关数据:
SolutionControlTreeData
为了不将私有数据暴露在外部类中,统一收纳在 Internal 内部类中管理。
三、为啥用std::unique_ptr?
核心原因:std::unique_ptr 是 管理 Internal 内部类对象最安全、最适配 Pimpl 模式 的选择,贴合你的项目场景,优势明确且无多余开销,具体原因(直白不绕弯):
1. 自动管理内存,杜绝泄漏
unique_ptr 会在 TestTreeData 对象销毁时,自动调用 Internal 的析构函数、释放内存,无需你手动写 delete mTreeList,避免忘记清理导致的内存泄漏。
2. 贴合 Pimpl 模式的"唯一所有权"
Internal 是 TestTreeData 专属的内部实现,仅被当前 TestTreeData 对象拥有,不对外共享、不允许拷贝。unique_ptr 恰好保证"唯一所有权",禁止拷贝赋值,完美匹配你的代码设计(内部数据隐藏)。
3. 轻量无性能开销
unique_ptr 是轻量级智能指针,底层实现和裸指针几乎一致,没有额外的引用计数开销,适合树数据类(可能频繁创建/销毁节点),不影响程序运行效率。
4. 支持前向声明(关键适配)
你的代码中 class Internal; 是前向声明(只声明、不定义),unique_ptr 支持将前向声明的类作为模板参数,而 shared_ptr 等其他智能指针会直接编译报错,刚好适配你现有的代码结构。
-
无需手动管理 unique_ptr 的释放,析构函数无需额外写 delete mTreeList(手动写反而多余,甚至可能报错)。
-
若 TestTreeData 需要支持拷贝/赋值,需手动实现拷贝构造和赋值运算符(unique_ptr 禁止默认拷贝,贴合 Pimpl 模式"隐藏实现"的初衷,一般无需支持)。
-
Internal 类的析构函数需正常实现(哪怕是空实现),避免 unique_ptr 释放时出现未定义行为。
四、为啥不用shared_ptr?
你的代码场景(前向声明)
cpp
class TestTreeData: public data::TreeObjectData
{
private:
class Internal; // 仅前向声明(无类的具体实现)
std<Internal> mTreeList; // 正常编译
// std<Internal> mTreeList; // 直接编译报错
};
为什么 unique_ptr 可以,shared_ptr 不行?
「unique_ptr」:仅需要 "类存在" 的声明(前向声明足够),它的底层不需要知道 Internal 的具体大小、析构细节(编译时不检查,运行时再匹配)。
「shared_ptr」:需要知道 Internal 的具体结构(比如析构函数、内存大小),因为它要管理引用计数、自动释放内存,前向声明只说 "有这个类",没提供这些细节,编译器无法处理,直接报错。