没有朋友声明的Pimpl习语和内部对象协作

时间:2011-06-24 10:10:11

标签: c++ shared-ptr friend pimpl-idiom

我正在使用pimpl习语实现几个类,并且遇到了一些设计问题。

首先,我一直看到pimpl像这样做了

class Object
{
public:
    Visible();
    ~Visible();
 .. etc ..
private:
    class ObjectImpl *_pimpl;
};

我有几个使用这种方法的类,我的问题是这些类中的一些需要访问彼此的实现细节,但_pimpl指针是私有的。

任何人都可以看到声明_pimpl公开的缺点。显然,如果它是公开的,那么有人可能会意外(或故意)重新分配它。 (我忽略了这样一个事实:“私人”可能被#defined定义为“公共”并且无论如何都会授予访问权限。如果你这样做,那么你应该得到你得到的东西。)

我很欣赏我的设计可能存在缺陷,并欢迎任何评论。

我真的不喜欢使用朋友,我不确定他们甚至会帮忙,因为你不能在没有完全定义Object的情况下转发声明Object :: ObjectImpl。

 ...
 private:
    class ObjectImpl *_pimpl;
    friend class OtherObject::OtherObjectImpl; // this needs a fully qualified OtherObject
};

THX 标记

*更新 - 更多细节**

我有两个类,一个叫做Command,另一个叫做Results。我在Command上有方法返回结果向量。

命令和结果都使用了pimpl习惯用法。我希望结果的界面尽可能小。

class Command
{
public:
    void getResults( std::vector< Results > & results );
    void prepareResults( std::vector< Results > & results );
private:
    class CommandImpl *_pimpl;
};

class Results
{
public:
    class ResultsImpl;

    Results( ResultsImpl * pimpl ) :
        _pimpl( impl )
    {
    }

private
    ResultsImpl *_pimpl;
};

现在在Command :: getResults()中。我将ResultsImpl注入结果。在Command :: prepareResults()中我需要访问ResultsImpl。

微米。

4 个答案:

答案 0 :(得分:7)

我怀疑是否有充分理由将实现公开:您始终可以使用公共方法公开实现的功能:

class Object
{
public:
   Object();
  ~Object();

  int GetImplementationDetail();

private:
  std::unique_ptr< ObjectImpl > _pimpl;
};

int Object::GetImplementationDetail()
{
  return pimpl->GetImplementationDetail();
}

除了一个类应该只负责一件事,一件事,并且应该对其他类有最小的依赖性;如果您认为其他类应该能够访问您的Object的pimpl,那么您的设计可能存在缺陷。

在作者更新之后

编辑:虽然你的例子仍然相当模糊(或者至少我不能说出它的完整意图),但你似乎误解了这个成语,现在尝试应用它对于没用的情况。正如其他人指出的那样,'P'代表私人。您的结果类没有太多私有实现,因为它们都是公共的。所以要么尝试使用我上面提到的并且不要“注入”任何东西,要么只是一起摆脱pimpl并仅使用Result类。如果你的Result的类接口应该是如此之小,以至于它只是一个指向另一个类的指针,在这种情况下似乎并没有多大用处。

答案 1 :(得分:1)

为什么你的课程完全取决于彼此的细节?你可能会重新考虑你的设计。类应该依赖于抽象。

如果你真的认为你的设计正确,没有什么可以阻止你提供“源文件 - 私有”标题,如:

include/
   foo.h
src/
   foo.impl.h
   foo.c
   bar.c

然后在foo.c和bar.c中#include foo.impl.h

foo.c:
    #include "foo.impl.h"
    ...

bar.c:
    #include "foo.impl.h"
    ...

但同样:一般来说,

<强> Dependency Inversion Principle

  

一个。高级模块不应该依赖于低级模块。两者都应该取决于抽象。

     

B中。抽象不应该依赖于细节。细节应取决于抽象。

另外请务必查看SOLIDGRASP,特别是关于松散耦合的问题。

答案 2 :(得分:0)

只要其他类看不到其类型_pimple定义,制作数据成员public ObjectImpl就不会为您带来任何好处 - 这是Pimple为阻止 而设置的内容。

您可以做的是为您的Object课程添加一个私人界面,这样可以让合作班级为Object做任何他们需要做的事情。

当然,关于friend作为应尽可能少使用的工具的常见免责声明均适用。

答案 3 :(得分:0)

因此没有必要对朋友进行资格认定。

如果您只使用friend class OtherObject,则OtherObject类可以访问必要的内部。

就个人而言,我的Pimpl只是一个struct(数据包),我将这些方法保留在原始类中进行操作。