← 模式

PIMPL 手法

12345678910111213141516171819202122232425262728293031323334353637383940414243// foo.h - 头文件 #include <memory> class foo { public: foo(); ~foo(); foo(foo&&); foo& operator=(foo&&); private: class impl; std::unique_ptr<impl> pimpl; }; // foo.cpp - 实现文件 class foo::impl { public: void do_internal_work() { internal_data = 5; } private: int internal_data = 0; }; foo::foo() : pimpl{std::make_unique<impl>()} { pimpl->do_internal_work(); } foo::~foo() = default; foo::foo(foo&&) = default; foo& foo::operator=(foo&&) = default;

此模式采用 CC0 公共领域贡献 许可。

需要 c++11 或更新版本。

意图

移除对内部类实现的编译依赖,并缩短编译时间。

描述

当一个头文件发生变化时,任何 #include 该文件的文件都需要重新编译。对于类的头文件来说,即使这些变化只应用于类的私有成员,也需要重新编译。PIMPL 手法向头文件的使用者隐藏了私有成员,使得这些内部细节可以在不要求客户端代码重新编译的情况下进行更改。

第 5-17 行定义了一个类 foo,我们对它应用了 PIMPL 手法。这个类的定义只包含了类的公共接口和一个指向内部实现的指针。我们使用 std::unique_ptr第 16 行)来确保实现的生命周期得到正确管理,并在 foo 的构造函数中对其进行初始化(第 35 行)。

虽然内部实现类 impl 在头文件的第 15 行进行了声明,但它的定义出现在实现文件的第 22-32 行。这使得该类的定义可以在不要求 foo 的使用者重新编译的情况下进行修改。

我们在第 40 行显式地将 foo 的析构函数设为默认,这是必要的,因为析构函数需要能看到 impl 的完整定义(以便销毁 std::unique_ptr)。请注意,我们还在第 42-43 行显式地将移动构造函数和移动赋值运算符设为默认,以便 foo 可以被移动。要使 foo 可复制,我们还必须实现复制构造函数和复制赋值运算符。

注意std::make_unique 是在 C++14 中引入的。对于 C++11,你可以自己实现一个

贡献者

  • Joseph Mansfield
  • Mehdi Amini

最后更新

2017年12月9日

来源

在 GitHub 上 Fork 此模式

分享