PIMPL习惯用法说明

时间:2018-12-20 06:39:28

标签: c++ encapsulation pimpl-idiom

在尝试从头文件中删除所有实现细节时,我决定使用并尝试PIMPL习惯用法。大部分(如果不是全部)示例,例如cppreference,我已经看到了间接使用级别,这是除了封装之外我无法理解的原因。这些示例始终带有这种风味模式。

典型

//A.hpp
class A
{
public:
    A(int);
    //More non-static class methods
    void set(int);

private:
    struct a_impl;
    std::unique_ptr<struct a_impl> pimpl;
};

//A.cpp
struct A::a_impl
{
private:
    int i;
public:
    a_impl(int i) : i{i}{}
    //More non-static implementation class methods
    void set(int i) {this->i = i;}
};
A::A(int i) : std::make_unique<struct a_impl>(i) {}
A::set(int i) {pimpl->set(i);}  //????
A:://More indirect calls to non-static member functions through pointer

在某些时候,我开始想知道,如果我们谈论的是实现,为什么我需要所有这些级别的复杂性和间接性。没有所有这些间接调用,为什么不做最简单的事情呢?

为什么不呢?

    //A.hpp
class A
{
public:
    A(int);
    //.....
    void set(int);

private:
    struct a_impl;
    std::unique_ptr<struct a_impl> pimpl;
};

//A.cpp
struct A::a_impl
{
    int i;
}

A::A(int i) : std::make_unique<struct a_impl>() { pimpl->i = i; }
A::set(int i) { pimpl->i = i; } //!!!!

我想澄清一下:

1-是否所有这些样本都是出于教育和良好封装实践的目的而提出的?

2- 除了问题1之外,我是否还缺少其他充分的理由来增加这种级别的复杂性和开销?

3-或者我提出的替代方案不是PIMPL惯用语吗?

1 个答案:

答案 0 :(得分:4)

您自己的方法以及与之比较的方法只是两种不同的习惯用法。 Herb Sutter在GotW #100中列出了它们以及更多内容:

  

该类的哪些部分应该放入impl对象?有潜力   选项包括:

     
      
  • 将所有私有数据(但不是函数)放入impl;
  •   
  • 将所有私人成员纳入展示;
  •   
  • 将所有私有成员和受保护成员都加入了展示;
  •   
  • 将所有非虚拟私人成员纳入impl;
  •   
  • 将所有内容放入impl中,并将公共类本身编写为仅公共接口,每个接口都实现为简单的转发功能   (一个手柄/主体变体)。
  •   

每个都有其优点和缺点。对于您研究的情况,您的方法似乎更好。对于其他情况,具有行为而不只是状态的pimpl可能更合适。真的没有一种适合所有人。