开发者

pimpl idiom vs. bridge design pattern

开发者 https://www.devze.com 2022-12-21 21:52 出处:网络
I just noticed a new term pimpl idiom, what\'s the difference between this idiom with Bridge design pattern? I am confused about that.

I just noticed a new term pimpl idiom, what's the difference between this idiom with Bridge design pattern? I am confused about that.

I also noticed the pimpl idiom is always used for swap function, what's tha开发者_Go百科t? Could anybody give me an example?


PIMPL is a way of hiding the implementation, primarily to break compilation dependencies.

The Bridge pattern, on the other hand, is a way of supporting multiple implementations.

swap is a standard C++ function for exchanging the values of two objects. If you swap the pointer to the implementation for a different implementation, you are essentially changing the mechanism of the class at runtime.

But in its basic and common form, a class using PIMPL points to a single implementation, so there is no abstract class with distinct subclasses — just one class, forward declared, and compiled elsewhere. Changing the implementation class does not require any recompilation of sources that include the main header.

For example, say you have a lot of private member functions, private enums, and private data. And these private "bits" change fairly frequently as the class is developed and maintained. If the #include dependencies are such that touching this header file causes a large number of sources to be recompiled, you have a good candidate for PIMPL.

So the Bridge pattern is about object-oriented design, while the PIMPL idiom is about physical design of files.

(For more on physical design, I recommend the book Large-Scale C++ Software Design by John Lakos.)


I have used pointer impl to hide implementation details from public interface/include file Basically interface look like this:

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

Then somewhere in inside Interface::Impl is defined and implemented, but details not exposed.

as far as swap, this is the first time I hear it. Can you elaborate on it?


Pimpl: In short the pimpl pattern is really good for hiding the private implementation. If you wanted to expose your header files for clients that needed to build against your public interface but you didn't want to expose your private implementation details you could use the pimpl pattern to hide the details. You would do this for a few reasons but mainly your header file would not have to change when you change your private implementation details change which otherwise would force your clients to have to recompile. In this way you decouple along with hide your private implementation details. Usually you should hold the impl pointer in a RAII container like a unqiue pointer to make sure it is freed upon destruction.

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
    }

Swap probably came up because when you are doing an assignment operator for this class and implementing your own swap routine to assign the members you need to be aware of assigning this pointer as well

    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 is a totally separate pattern that is used to bridge items together. The similarity between the patterns would be that you may have a process method in your class that hides the actual call since it will delegate this call to the contained object that will handle the actual method call. In other words if you had a request interface base class that contained a request implementor base class pointer. Therefore, at run time you could "bridge" the systems by initializing the bridge with a specific request implementor type but someone calling the bridge would just call the process request method which would delegate the call to the specific derived implementor request method at run time. There are several sources on google with nice diagrams that can explain this more clearly as well.


There is currently a Boost library under review that implements the Pimpl pattern. I've cobbled together a few basic examples using the proposed Boost Pimpl implementation if anyone wants a jump on using this code internally:

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


UPDATE: The above link has been updated to point to the archived version. It doesn't seem likely that Boost.Pimpl will be accepted in to boost at this point in time in favor of std::unique_ptr<> as a viable replacement (albeit less complete than Boost.Pimpl for some less common usecases).

If you have access to C++11, you are probably better off using std::unique_ptr<> as a PIMPL implementation:

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

You can see a full example using std::unique_ptr<> in my C++11 reentrant class locking strategy question.

Using std::unique_ptr<>'s swap() method, it becomes convenient and very practical for C++11's move semantics to move the guts of an object from one owner to another. For example, suppose MyClass exports a swap() implementation that just forwards to std::unique_ptr<>'s swap:

void MyClass::swap(MyClass *c) { impl_.swap(c); }
MyClass c1, c2;
c1.swap(c2);

This is now an exception safe way of transferring c2's contents to c1. Another great reason to use a the PIMPL idiom is to preserve/maintain a stable ABI.


Well, here is PIMPL idiom: http://en.wikipedia.org/wiki/Opaque_pointer It is pretty clear what it does.

And Bridge Pattern is more involved - it does not just hold data. http://en.wikipedia.org/wiki/Bridge_pattern#C.2B.2B

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号