Pimpl 设计模式(指针指向实现)

一、背景

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;
};

二、原因

InternalPimpl设计模式 的实现,用于存放 TestTreeData 的所有私有数据,将内部实现与对外接口分离,让外部类保持简洁干净。


逐部分解析

1. class Internal;

  • 仅为前向声明

  • 作用:告诉编译器存在这个类,具体定义后续提供

2. std::unique_ptr<Internal> mTreeList;

  • 管理内部对象的智能指针

  • TestTreeData 唯一的私有成员,结构极简

3. TestTreeData::Internal 实现类

  • 真正存放内部数据的容器

  • 当前存储:SolutionControlTreeData(求解设定树节点数据)

  • 所有私有成员变量、内部状态都放在这里


这种写法的3个核心优势

  1. 头文件干净整洁

外部类看不到内部私有成员,接口清晰

  1. 降低代码耦合

内部依赖的类型不暴露给外部,加快编译速度

  1. 易于扩展维护

新增成员变量只需修改 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 等其他智能指针会直接编译报错,刚好适配你现有的代码结构。

  1. 无需手动管理 unique_ptr 的释放,析构函数无需额外写 delete mTreeList(手动写反而多余,甚至可能报错)。

  2. 若 TestTreeData 需要支持拷贝/赋值,需手动实现拷贝构造和赋值运算符(unique_ptr 禁止默认拷贝,贴合 Pimpl 模式"隐藏实现"的初衷,一般无需支持)。

  3. 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 的具体结构(比如析构函数、内存大小),因为它要管理引用计数、自动释放内存,前向声明只说 "有这个类",没提供这些细节,编译器无法处理,直接报错。

相关推荐
yaoxin5211232 小时前
434. Java 日期时间 API - Period 基于日期的时间段
java·开发语言·python
凡人叶枫2 小时前
Effective C++ 条款30:透彻了解 inlining 的里里外外
linux·开发语言·c++·嵌入式开发·effective c++
noipp2 小时前
推荐题目:洛谷 P10907 [蓝桥杯 2024 国 B] 蚂蚁开会
c语言·c++·算法·编程·洛谷
学逆向的3 小时前
C++纯虚函数
开发语言·c++·网络安全
程序员二叉3 小时前
【JUC】ThreadLocal底层原理|内存泄漏|弱引用|跨线程传递方案
java·开发语言·面试·职场和发展·juc
程序员二叉3 小时前
【JUC】线程池全套深度详解|参数|流程|拒绝策略|调优|异常处理
java·开发语言·jvm·算法·面试·juc
凡人叶枫4 小时前
Effective C++ 条款22:将成员变量声明为 private
linux·开发语言·c++
Qt程序员4 小时前
掌握 Linux 内核调度:从原理到实现(进程篇)
java·开发语言
code bean4 小时前
【LangChain】检索器完全指南:从向量检索到生产级 RAG 架构
java·开发语言·微服务
LabVIEW开发4 小时前
LabVIEW + MATLAB 混合编程:爆炸场测试数据精准采集方案
开发语言·matlab·labview