pImpl技巧:接口与实现分离

举报
技术火炬手 发表于 2020/07/20 16:13:38 2020/07/20
【摘要】 最近在实现某个模块加强了对pImpl设计模式的理解,记录分享给大家。

最近在实现某个模块加强了对pImpl设计模式的理解,这里记录下来备忘。

pImpl模式用来将接口与实现分离,更改实现的话只需要重新编译cpp实现,不影响依赖其抽象的模块,也就是著名的桥接模式。

然而在实践中发现有很多类嵌套类,这种也算是pImpl的体现,但是做的不够完美,因为这些嵌套类直接定义在头文件中,头文件需要暴露出去,其他模块自然也会看到这些内部嵌套类的定义,影响依赖关系。比如:

// Module.h
class Module {
public:
    ...
private:
    class InternalModule { // Module部分实现委托到InternalModule内部类
        ...
    } m_module;
};

这个模式核心所在是在头文件通过前向声明class Impl,并通过指针指向实例,然后在cpp中定义并实现Impl。

// Person.h
#include <memory>
class Person {
public:
    Person(std::string name); // (1)
    ~Person(); // (2)
    void Dosomething();
    ...
private:
    struct Impl; // (3)
    std::unique_ptr<Impl> pImpl; // (4)
};

// Person.cpp
#include "Person.h"
class Person::Impl { // impl定义与实现
public:
    Impl(std::string name): name(name) { }
    void Dosomething() {
        // ...
    }
private:
    std::string name;
};

void Person::Dosomething() {
    return pImpl->Dosomething();
}

// 构造、析构函数必须放在实现cpp里,避免inline constructor/destructor导致的incomplete type `Impl'问题
Person::Person(std::string name):
    pImpl(new Impl{name})  // 编程规范问题,可以放到Init()函数中初始化
    { }
Person::~Person() = default;

需要注意几个点,由于采用std::unqiue_ptr托管pImpl,而std::unqiue_ptr的声明如下:

template <typename _Tp, typename _Dp = default_delete<_Tp>>
class unique_ptr;

其中default_delete模板实例化需要看到Impl的定义,而其定义在cpp中,导致构造、析构函数必须得放到cpp中:

(1) 构造函数需要在头文件中声明,cpp中实现,而初始化pImpl可以放到Init函数中

(2) 析构函数同上

(3) 在类内前向声明Impl,避免暴露实现出去

(4) 使用std::unique_ptr托管pImpl。

作者:netcan

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。