【问题标题】:Pimpl idiom and internal object collaboration without friend declarationPimpl 成语和内部对象协作没有朋友声明
【发布时间】:2011-09-21 22:06:43
【问题描述】:

我正在使用 pimpl 习惯用法实现几个类,但遇到了一些设计问题。

首先,我总是看到 pimpl 这样做

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

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

任何人都可以看到将 _pimpl 公开的不利之处吗?显然,如果它是公开的,那么有人可能会意外(或故意)重新分配它。 (我忽略了“私人”可以被#定义为“公共”并授予访问权限这一事实。如果你这样做,那么你应该得到你所得到的)。

我感谢我的设计可能存在缺陷,并且也欢迎任何符合这些思路的 cmets。

我真的很讨厌使用朋友,并且不确定他们是否会提供帮助,因为您无法在没有完全定义 Object 的情况下转发声明 Object::ObjectImpl。

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

谢谢 标记。

* 更新 - 更多细节 **

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

Command 和 Results 都使用 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。

M.

【问题讨论】:

  • 我认为需要更多细节。

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


【解决方案1】:

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

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

  int GetImplementationDetail();

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

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

除此之外,一个类应该只负责一件事,只负责一件事,并且应该对其他类具有最低限度的依赖;如果您认为其他类应该能够访问您的对象的 pimpl,那么您的设计可能有缺陷。

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

【讨论】:

  • +1: 与unique_ptr 有很好的接触 :) 请注意,即使最终效果非常相似,它也可以提供“自定义删除器”(而不是定义析构函数)。
  • @sbi:使用正确的析构函数自动处理内存并免费移动构造函数/赋值
  • @sbi:shared_ptr 经常(不适)用于 Pimpl,而没有适当考虑它所暗示的共享语义:/
  • @Matthieu:我不相信隐式移动语义比隐式共享语义好得多。我错过了什么?
  • @sbi:当一个对象被“移动”时,仍然有一个唯一的所有者(正如unique_ptr的名字所暗示的那样),因此我们仍然在谈论“值对象”,并且只优化它路过。另一方面,共享所有权意味着多个“引用对象”共享相同的底层数据,因此更改为一个会反映在其他数据上。
【解决方案2】:

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

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

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

A.高级模块不应依赖于低级模块。两者都应该依赖于抽象。

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

还要确保查看SOLIDGRASP,尤其是关于松散耦合的要点。

【讨论】:

  • 您是否建议我有一个 ResultsImplInterface,然后我可以在 Results 的公共 getter 方法中返回它。因此仍然保持实际的实现私有?。
  • 如果你把它放在一个指针或引用中,只是为了稍后将它转发给另一个类,那么这将是适用的。
  • 实际上,让类依赖于接口并不能解决我眼前的问题。它不会解决我必须访问私有的结果类 pimpl 成员的问题。使 impl 对象成为接口并不会改变它的保护。
【解决方案3】:

创建数据成员 _pimple public 不会给您带来任何好处,只要其他类看不到其类型 ObjectImpl定义 - 这正是 Pimple 想要预防的事情

您可以做的是向您的Object 类添加一个私有接口,这将允许结识的类使用Object 做任何他们需要做的事情。

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

【讨论】:

    【解决方案4】:

    没有必要这样限定朋友。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-16
      • 1970-01-01
      • 2023-03-21
      • 2011-02-04
      • 1970-01-01
      相关资源
      最近更新 更多