C++ PImpl模式、指向实现的指针、PImpl Idiom、隐藏实现细节

C++ PImpl模式、指向实现的指针、PImpl Idiom、隐藏实现细节

flyfish

PImpl 全称是 “Pointer to Implementation”,在中文中通常翻译为“指向实现的指针”或者“指向实现”。PImpl 是一种编程技巧,通常用于 C++ 中,通过这种技术,可以隐藏类的实现细节,达到信息隐藏和二进制兼容性的目的。PImpl 也被称为“编译防火墙”(Compilation Firewall)。

PImpl 模式在遇到以下情况下可以使用:

如果是在简单场景下,不使用 PImpl 的实现更直接,如果遇到下面的情况,就可以使用PImpl 的方式。

1 隐藏实现细节:

如果你想要隐藏类的内部实现细节,使得用户只需要知道类的接口,而无需了解具体实现。
这在发布库或 API 时特别有用,可以避免用户代码依赖于类的具体实现,从而提高封装性。

2 减少编译依赖:

如果你的类实现可能经常改变,而不希望每次改变都导致所有依赖该类的代码重新编译。
例如,当一个类的成员变量或内部实现发生变化时,使用 PImpl 模式可以避免重新编译所有包括这个头文件的文件。

3提高二进制兼容性:

在需要保持 ABI(应用程序二进制接口)稳定的场景下,PImpl 模式可以在不改变类接口的前提下,修改类的实现细节,从而提高二进制兼容性。 如果你需要确保类的接口稳定,并希望在不改变接口的情况下能够自由地修改实现细节,PImpl 是一个合适的选择。

4 管理复杂实现:

当类的实现变得非常复杂时,将实现细节放在单独的实现类中,有助于保持代码的清晰和可维护性。

其他名字

Cheshire Cat:这个名字来源于《爱丽丝梦游仙境》中的柴郡猫,它有时会隐形,只留下笑脸。这个比喻形象地描述了 PImpl 模式隐藏实现细节的特性。
Compiler Firewall:这个名字强调了 PImpl 模式可以减少编译依赖,像防火墙一样隔离实现和接口。
d-pointer:这个名字主要在 Qt 库中使用,指的是 “指向数据(implementation)的指针”。

+---------------------+       +---------------------+
|      MyClass        |       |    MyClass::Impl    |
|---------------------|       |---------------------|
| - pImpl: Impl*      |       | - someData: int     |
|---------------------|       |---------------------|
| + MyClass()         |       | + Impl()            |
| + ~MyClass()        |       | + ~Impl()           |
| + someMethod()      |       | + someMethodImpl()  |
+---------------------+       +---------------------+
           |                                 |
           +-------------------+-------------+
                               |
                         has a |
                               |
                      +--------v---------+
                      |    std::unique_ptr|
                      |-------------------|
                      |     - Impl        |
                      +-------------------+

C++ 里的 “has a”

在 C++ 编程中,“has a” 关系指的是成员对象或成员指针的关系。也就是说,一个类拥有另一个类的对象或指针作为其成员。这种关系与继承(“is a”)不同:
“has a”(组合关系):一个类包含另一个类的对象或指针。
“is a”(继承关系):一个类继承自另一个类。
PImpl 模式中,“has a” 关系意味着 MyClass 包含一个 Impl 对象的指针,这个关系表示 MyClass 持有并管理 Impl 对象的生命周期。

示例代码

头文件:MyClass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#include <memory> // 使用智能指针需要包含这个头文件

class MyClass {
public:
    MyClass(); // 构造函数
    ~MyClass(); // 析构函数

    void someMethod(); // 公有方法

private:
    // 前向声明一个名为 Impl 的类,这是实现细节类
    class Impl;
    // 使用 std::unique_ptr 管理 Impl 的指针
    std::unique_ptr<Impl> pImpl;
};

#endif // MYCLASS_H
源文件:MyClass.cpp
#include "MyClass.h" // 包含头文件

// 定义实现类 Impl
class MyClass::Impl {
public:
    // 实现方法
    void someMethodImpl() {
        // 具体的实现细节
        // 例如,这里我们简单地输出一段文字
        std::cout << "This is the implementation of someMethod." << std::endl;
    }
};

// 构造函数
MyClass::MyClass() : pImpl(std::make_unique<Impl>()) {
    // 初始化 pImpl,指向一个新的 Impl 对象
}

// 析构函数
MyClass::~MyClass() = default; // 使用默认析构函数,这里会自动释放 pImpl 指向的内存

// 公有方法,调用 Impl 类的实现方法
void MyClass::someMethod() {
    pImpl->someMethodImpl(); // 通过 pImpl 调用具体实现
}
主程序文件:main.cpp
#include "MyClass.h" // 包含 MyClass 类的头文件
#include <iostream> // 用于输出

int main() {
    MyClass myClass; // 创建 MyClass 对象
    myClass.someMethod(); // 调用公有方法
    return 0; // 程序结束
}

程序输出:

This is the implementation of someMethod.

说明

  1. 头文件 (MyClass.h)
    class MyClass 定义了一个类,其中有一个私有的指向实现类 Impl 的指针 pImpl
    构造函数和析构函数声明,以及一个公有方法 someMethod 的声明。

  2. 源文件 (MyClass.cpp)
    首先定义 MyClass 的实现类 Impl。这是一个具体实现类,其中包含方法 someMethodImpl
    MyClass::MyClass 构造函数初始化 pImpl,使用 std::make_unique 创建一个新的 Impl 对象。
    MyClass::~MyClass 析构函数默认删除 pImpl,因此不需要手动管理内存。
    MyClass::someMethod 方法调用 pImpl 指向的 Impl 对象的 someMethodImpl 方法。

  3. 主程序文件 (main.cpp)
    创建 MyClass 对象并调用其 someMethod 方法。

  • 38
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
pimpl(Pointer to Implementation)是一种设计模式,用于隐藏类的实现细节。该模式的主要目的是将类的接口和实现分离,以便提高代码的可维护性和可扩展性。 在传统的C++开发中,类的实现细节通常会暴露在类的头文件中,这样会导致头文件的内容变得庞大且混乱。而pimpl模式通过在类中使用指向实现类的指针,将实现细节脱离类的接口部分,使得头文件只需包含一个简单的指针声明,从而实现隐藏指针的效果。 使用pimpl模式隐藏指针有多个好处。首先,它可以提高编译速度,因为只有头文件的改变才会导致需要重新编译的文件数量减少;其次,它可以减少对外部用户的依赖,当类的实现发生变化时,只需要重新编译实现文件而无需重新编译使用该类的其他文件;此外,pimpl模式还可以提高二进制兼容性,因为只有指针的大小发生变化,而不是整个类的大小。 使用pimpl模式时,首先需要在类的头文件中声明一个指向实现类的指针,并在类的实现文件中定义实现类。然后,在类的构造函数和析构函数中创建和销毁实现类的对象,并在类的成员函数中通过指针访问实现类的成员。 因此,pimpl模式允许将实现细节从类的接口中分离出来,提高了代码的可维护性和可扩展性。使用pimpl隐藏指针的设计模式可以提高编译速度、减少对外部用户的依赖以及提高二进制兼容性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西笑生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值