5

pimpl 惯用语中使用的实现类是否有任何理由拥有任何私有成员?我真正能想到的唯一原因是保护自己免受自己的伤害——即私有成员用于执行类和用户之间的某种契约,在这种情况下,类和用户是密切相关的,所以它似乎没有必要。

4

7 回答 7

11

我认为人们将 Pimpl成语与 Adapter/Bridge/Strategy模式混淆了。习语是特定于一种语言的。模式可以适用于多种语言。

Pimpl 习惯用法旨在解决 C++ 中的以下问题: 类的私有成员在类声明中可见,这为类的用户添加了不必要的#include 依赖项。这个习语也被称为编译器防火墙

如果实现是直接写在外部类对应的 *.cpp 文件中,并且在模块外无法访问,那么我认为简单地使用 Pimpl 类的结构是完全可以的。为了进一步强化实现并不意味着直接重用的想法,我将它们定义为私有内部结构:

// foo.h
class Foo : boost::noncopyable
{
public:
   ...

private:
   struct Impl;
   boost::scoped_ptr<Impl> impl_;
};

// foo.cpp
struct Foo::Impl
{
   // Impl method and member definitions
};

// Foo method definitions

一旦有了实现类的头文件,我想我们就不再谈论 Pimpl 成语了。我们是在谈论适配器、桥接器、策略、接口类等......

只是我的2美分。

于 2010-02-01T20:36:30.867 回答
6

取决于您对 pImpl 的实现 - 特别是在您强制执行类不变量的地方,但总的来说,我认为 impl 部分不需要具有受保护/私有成员。实际上,我通常将其声明为结构。

于 2010-02-01T16:51:57.290 回答
4

理论上,pimpl 类仍然只是一个与其他类一样的类。它是接口的具体实现并不意味着其他代码不是 pimpl 类本身的客户端。

也就是说,在实践中,我发现 pimpl 类往往更接近于具有一些成员函数的结构,而不是完整的对象,并且不需要将接口与实现分开。

于 2010-02-01T16:49:12.447 回答
2

为什么它不应该有私人成员?仅仅因为您将一个接口定义为 PIMPL 并不意味着您在其他任何时候都不想使用该类。

还是一堂课。数据可能应该是私有的或受保护的。对公众、私人或受保护者永远无法访问的数据的操作。您可能希望公开、保护或公开的操作。

于 2010-02-01T16:41:28.397 回答
1

我真正能想到的唯一原因是保护自己免受自己的伤害

这就是为什么首先存在“私有”和“受保护”的原因。当然,你应该在你的实现中使用它们——唯一一次我不会是如果实现没有行为(在这种情况下它不是真正的实现)。

于 2010-02-01T17:15:52.337 回答
0

(我误解了这个问题,所以我正在改变我的答案。)

由带有 pimpl 的类指向的实现类应该是一个常规类,与其他任何隐藏私有细节的理由一样多。事实上,它通常是一个预先存在的类,后来添加了 pimpl 层以打破依赖关系,并可能稍微简化接口。

于 2010-02-01T16:53:16.243 回答
0

因为private意味着更好的数据封装。

我知道这看起来很傻,但是我们有一种在工作中定义接口的方法非常简单:

class Interface;

class Concrete { public: .... private: Interface* m_interface; }

class ConcreteFoo: public Interface {};

主要优点:您可以随时更换另一个ConcreteBar。实际上,它是PimplStrategy模式的组合,并且Concrete将调用 a的构造函数Factory来负责为有效对象提供服务。

如果您想不出第二种实现 heart 的方法,只需将其封装在一个类中即可。这样,如果你以后必须重构,你只需Interface要用完全相同的方法集组成一个抽象,改变一些指针(和构造函数),你会没事的;)

于 2010-02-01T18:57:51.640 回答