带有包含类的标头的Pimpl

Pimpl with header containing the class

本文关键字:Pimpl 包含类      更新时间:2023-10-16

我遇到了一个实现,它将pimpl类作为头,并将其包含在pimpl实现中。这有道理吗?类似这样的东西:

UI.h

class UI {
public:
   UI();
   virtual ~UI();
   // bunch of methods
private:
      UIImpl* m_impl;
}

UIImpl.h

class UIImpl
{
public:
  UIImpl( ...) ;
  ......
}

UIImpl.cpp

#include "UIImpl.h" 
UIImpl::UIImpl()
{
  //Actual Implementation
  ...
}

我认为PIMPL的原因是将实现完全隐藏在cpp文件中。头球攻门会破坏目的吗?

它们是不同类型的头。UI.h是"公共"的——它是库的外部接口的一部分,供客户端使用。

UIImpl.h是"私有的",就像UIImpl.cpp一样。只要它从未包含在公共标头中,它就可以对客户端保持不可见,就像.cpp本身一样。

impl类定义拆分为头文件可能有几个原因。也许其他一些实现类被传递了UIImpl&参数。也许只在头上运行doxygen更容易。也许这只是项目政策。

底线是,它仍然是Pimpl,只要你不发布私有头。

我认为PIMPL的原因是将实现完全隐藏在cpp文件中。头球攻门会破坏目的吗?

不一定。pIMPL可以用于快速值交换(可能在C++11/move语义之前的体系结构中)、桥接设计模式或任何其他原因。

桥梁设计模式示例:

class WinImpl { virtual ~WinImpl() = 0; /* other interfaces here */ };
// different header file(s)
#ifdef WIN32
class WindowsWinImpl: public WinImpl { /* ... */ };
#else
class LinuxWinImpl: public WinImpl { /* ... */ };
#endif
// different header file
class Window { private: WinImpl* pImpl /*= OSWindowFactory::CreateImpl();*/ };

在这种情况下,您有一个pImpl模型,在Window类的头中包含WinImpl的定义是完全可以的(因为目的不是隐藏实现,而是抽象它的接口)。

这样做非常合理。它允许修改UIImpl.h(和相关的.cpp),而不必更改依赖于UI.h的代码。由于UIimpl类只是作为一个指针存储[并且假设UI本身只能访问UIimpl类的公共方法,因此UI.h代码对UIimpl类一无所知

事实上,您可能需要"UIimpl.h"来允许UI类查看该类的公共功能。

例如

class UIImpl
{
public:
     UIImpl( ...) ;
     void func() { ... }
}
class UI 
{
  public:
   UI();
   virtual ~UI();
   // bunch of methods
   void func() { m_impl->func(); }
   ... 
}

为此,UI需要知道UIimpl类提供的公共接口。

是的,这确实有意义。

一个使用示例:在为多个操作系统开发的代码中,UI.h从独立于操作系统的代码中可见,而不是UIImpl.h。操作系统相关代码(实现代码)将同时看到标题UI.h和UIImpl.h