Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/138.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 实践中的Pimpl成语_C++_Pimpl Idiom - Fatal编程技术网

C++ 实践中的Pimpl成语

C++ 实践中的Pimpl成语,c++,pimpl-idiom,C++,Pimpl Idiom,关于pimpl习语有一些问题,但我更好奇的是它在实践中的使用频率 我知道在性能和封装之间有一些折衷,加上额外的重定向带来的一些调试麻烦 那么,这是应该在每个类的基础上采用,还是应该在全有或全无的基础上采用?这是最佳实践还是个人偏好 我意识到这有点主观,所以让我列出我的首要任务: 代码清晰度 代码可维护性 演出 我总是假设我需要在某个时候将代码公开为库,所以这也是一个考虑因素 编辑:完成相同任务的任何其他选项都是欢迎的建议。这个习惯用法对大型项目的编译时间有很大帮助 我想说的是,你是按照每节

关于pimpl习语有一些问题,但我更好奇的是它在实践中的使用频率

我知道在性能和封装之间有一些折衷,加上额外的重定向带来的一些调试麻烦

那么,这是应该在每个类的基础上采用,还是应该在全有或全无的基础上采用?这是最佳实践还是个人偏好

我意识到这有点主观,所以让我列出我的首要任务:

  • 代码清晰度
  • 代码可维护性
  • 演出
我总是假设我需要在某个时候将代码公开为库,所以这也是一个考虑因素


编辑:完成相同任务的任何其他选项都是欢迎的建议。

这个习惯用法对大型项目的编译时间有很大帮助


我想说的是,你是按照每节课来做,还是按照全有或全无的原则来做,首先取决于你为什么选择pimpl这个成语。我在建立图书馆时的理由如下:

  • 想要隐藏实现以避免泄露信息(是的,这不是一个自由和开放源码软件项目:)
  • 想要隐藏实现以减少客户端代码的依赖性。如果您构建一个共享库(DLL),则可以更改pimpl类,而无需重新编译应用程序
  • 希望减少使用库编译类所需的时间
  • 想要修复名称空间冲突(或类似问题)
所有这些原因都不提示采用“要么全有,要么全无”的方法。在第一种情况下,您只需对要隐藏的内容进行pimplize,而在第二种情况下,对您希望更改的类进行pimplize可能就足够了。同样出于第三和第四个原因,隐藏非平凡的成员(例如,第三方库,甚至STL)只会带来好处

无论如何,我的观点是,我通常不会觉得这样的东西太有用:

class Point {
  public:      
    Point(double x, double y);
    Point(const Point& src);
    ~Point();
    Point& operator= (const Point& rhs);

    void setX(double x);
    void setY(double y);
    double getX() const;
    double getY() const;

  private:
    class PointImpl;
    PointImpl* pimpl;
}

在这种情况下,折衷开始出现,因为指针需要取消引用,而方法不能内联。但是,如果只对非平凡类执行此操作,则通常可以容忍轻微的开销,而不会出现任何问题。

我通常在希望避免头文件污染代码库时使用它。h就是一个完美的例子。它的行为如此恶劣,我宁愿自杀也不愿让它到处可见。因此,假设您想要一个基于类的API,将其隐藏在pimpl类后面可以很好地解决这个问题。(如果您满足于只公开单个函数,那么这些函数当然可以被前向声明,而无需将它们放入pimpl类中)


我不会在任何地方都使用pimpl,部分原因是性能受到了影响,部分原因是这是为了一个通常很小的好处而进行的大量额外工作。它提供给您的主要内容是实现和接口之间的隔离。通常,这不是一个很高的优先级。

pImpl在实现std::swap和operator=时非常有用,具有很强的异常保证。我倾向于说,如果您的类支持这两个字段中的任何一个,并且有多个非平凡字段,那么它通常不再取决于首选项

否则,问题在于您希望客户端通过头文件绑定到实现的紧密程度。如果二进制不兼容的更改不是问题,那么在可维护性方面您可能不会受益很多,尽管如果编译速度成为一个问题,通常可以节省成本

性能成本可能更多地与内联的损失有关,而不是与间接性有关,但这是一个大胆的猜测

您可以随时稍后添加pImpl,并声明从今天起,客户端将不必仅仅因为您添加了一个私有字段而重新编译


因此,所有这些都不意味着一种全有或全无的方法。你可以有选择地为那些能给你带来好处的课程做这件事,而不是为那些没有好处的课程做这件事,然后改变你的想法。例如,将迭代器实现为pImpl听起来太多了……

我在自己的库中的几个地方使用了这个习惯用法,在这两种情况下,都是为了清晰地将接口从实现中分离出来。例如,我有一个在.h文件中完全声明的XML读取器类,它有一个到RealXMLReader类的PIMPL,RealXMLReader类在非公共的.h和.cpp文件中声明和定义。RealXMlReader又是我使用的XML解析器(目前为Expat)的方便包装器

这种安排允许我将来从Expat转换到另一个XML解析器,而无需重新编译所有客户机代码(当然,我仍然需要重新链接)


注意,我这样做并不是出于编译时性能的原因,只是为了方便起见。有一些PIMPL fabnatics坚持认为任何包含三个以上文件的项目都是不可编译的,除非您始终使用PIMPL。值得注意的是,这些人从来没有产生任何实际的证据,而只是模糊地引用“C++”和“指数时间”。几乎每个Qt类都使用类似于pimpl的“D”指针。这允许在不破坏ABI的情况下执行更简单的更改。

代码清晰 代码清晰性是非常主观的,但在我看来,具有单个数据成员的头比具有多个数据成员的头可读性好得多。然而,实现文件的噪音更大,因此清晰度降低。如果类是基类(主要由派生类使用,而不是由维护类使用),那么这可能不是问题

维修性 对于pimpl'd类的可维护性,我个人认为在每次访问数据成员时额外的取消引用是乏味的。如果数据是纯私有的,那么访问器就帮不上忙,因为无论如何,您都不应该为它公开访问器或变体,而且