pimpl成语与桥梁设计模式

时间:2010-02-27 03:55:51

标签: c++ design-patterns

我刚注意到一个新术语pimpl成语,这个成语与Bridge设计模式有什么区别?我很困惑。

我也注意到pimpl成语总是用于交换功能,那是什么?有人可以举个例子吗?

5 个答案:

答案 0 :(得分:49)

PIMPL是一种隐藏实现的方式,主要是为了打破编译依赖性。

另一方面,Bridge模式是一种支持多种实现的方式。

swap是用于交换两个对象的值的标准C ++函数。如果将指针交换到不同实现的实现,则实质上是在运行时更改类的机制。

但是在其基本和通用形式中,使用PIMPL的类指向单个实现,因此没有具有不同子类的抽象类 - 只有一个类,前向声明,并在其他地方编译。更改实现类不需要重新编译包含主标题的源。

例如,假设您有很多私有成员函数,私有枚举和私有数据。随着课程的开发和维护,这些私人“位”会相当频繁地发生变化。如果#include依赖关系是触及此头文件会导致重新编译大量源,那么您就有了很好的PIMPL候选者。

所以Bridge模式是关于面向对象的设计,而PIMPL习惯是关于文件的物理设计。

(有关物理设计的更多信息,我推荐约翰拉科斯的书Large-Scale C++ Software Design。)

答案 1 :(得分:3)

我使用指针impl隐藏了public interface / include文件的实现细节 基本上界面看起来像这样:

struct Interface {
private:
class Impl;
Impl *pImpl;
};

然后定义并实现了Interface::Impl内部的某个地方,但细节未公开。

就交换而言,这是我第一次听到它。你能详细说明吗?

答案 2 :(得分:3)

Pimpl:简而言之,pimpl模式非常适合隐藏私有实现。如果您希望为需要针对您的公共接口构建的客户端公开头文件,但您不想公开您的私有实现详细信息,则可以使用pimpl模式隐藏详细信息。您可以出于某些原因执行此操作,但主要是您更改私有实现详细信息更改时不必更改头文件,否则会导致客户端不得不重新编译。通过这种方式,您可以分离隐藏私有实现细节。通常你应该将impl指针放在RAII容器中,就像unqiue指针一样,以确保在销毁时释放它。

Pimpl

    // my_class.h
    class my_class {
       public:
        // public here
    private:
       class impl;  //forward declare impl
       unique_ptr<impl> pimpl; // private implementation pointer
    };


    // my_class.cpp
    class my_class::impl {  // defined privately here
      // ... all private data and functions: all of these
      //     can now change without recompiling callers ...
    };
    my_class::my_class(): pimpl( new impl )
    {
      // ... set impl values ... 

       my_class& operator=(my_class other);
       friend void swap (my_class& lhs, myclass& rhs);  // used for assignment
    }

交换可能会出现,因为当您为此类执行赋值运算符并实现自己的交换例程来分配成员时,您需要知道分配此指针

    my_class& my_class::operator=(my_class other)
    {
      swap(*this,other);
      return  *this;
    }

    void swap ( my_class& lhs, myclass& rhs )
    {
      using std::swap // now use default swap if override doesn't exist

      // call swap for other members
     //  swap (lhs.data,rhs.data);

      // call swap on unique_ptr
      lhs.pimpl.swap(rhs.pimpl);  // doesn't throw exceptions

    }

Bridge 是一个完全独立的模式,用于将项目连接在一起。模式之间的相似之处在于,您的类中可能有一个隐藏实际调用的进程方法,因为它会将此调用委托给将处理实际方法调用的包含对象。换句话说,如果您有一个包含请求实现者基类指针的请求接口基类。因此,在运行时,您可以通过使用特定请求实现者类型初始化桥来“桥接”系统,但是调用桥的人只会调用进程请求方法,该方法将在运行时将调用委托给特定的派生实现者请求方法。谷歌有几个很好的图表来源,可以更清楚地解释这一点。

答案 3 :(得分:1)

嗯,这是PIMPL习语:http://en.wikipedia.org/wiki/Opaque_pointer很清楚它是做什么的。

Bridge Mode更为复杂 - 它不仅仅包含数据。 http://en.wikipedia.org/wiki/Bridge_pattern#C.2B.2B

答案 4 :(得分:1)

目前正在审核一个实施Pimpl模式的Boost库。如果有人想在内部使用此代码,我已经使用提议的Boost Pimpl实现拼凑了一些基本示例:

https://github.com/sean-/Boost.Examples/tree/a148be39abcb21428857aa50495f8c352600741e/pimpl


更新:上述链接已更新为指向存档版本。 Boost.Pimpl似乎不太可能在此时被提升以支持std::unique_ptr<>作为一个可行的替代品(虽然对于一些不太常见的用例而言不如Boost.Pimpl完整)。 p>

如果您有权访问C++11,那么最好使用std::unique_ptr<>作为PIMPL实施:

class MyClass {
 public:
  // ...
 private:
  class Impl;
  std::unique_ptr<Impl> impl_;
};

您可以在我的C++11 reentrant class locking strategy问题中看到使用std::unique_ptr<>的完整示例。

使用std::unique_ptr<>的{​​{1}}方法,swap()移动语义将对象的内容从一个所有者移动到另一个所有者变得方便且非常实用。例如,假设C++11导出MyClass实施,该实施只转发到swap()的{​​{1}}:

std::unique_ptr<>

现在这是将swap的内容转移到void MyClass::swap(MyClass *c) { impl_.swap(c); } MyClass c1, c2; c1.swap(c2); 的异常安全方式。使用PIMPL习惯用法的另一个重要原因是保持/维持稳定的ABI。