I have read a bit about the PIMPL idiom and was wondering - is it any different to forward declaring the dependent type(s)?
If so:
- When will I prefer using that over a forward declaration?
- Do these two versions differ in their compilation time?
- Is one of them more scalable than the other?
Specifically consider a class Foo
that is dependent on Bar
(should have a member of type Bar
).
Foo.h
with forward declaration:
class Bar;
class Foo
{
public:
Foo();
private:
Bar* _bar;
};
Foo.h
with PIMPL:
class Foo
{
public:
Foo();
private:
/* FooImpl is an incomplete type at this point.
* Implemented in cpp file and has a member of type Bar.
*/
class FooImpl;
FooImpl* _fooImpl;
}
Please ignore the raw pointer usage - I was just trying to make a point.
Yes, they are different. The PIMPL idiom (it has several names) is specifically about hiding the implementation detail from the client code. This could be done for a number of reasons, including (but not limited to);
In essence, the PIMPL offers a technique to "hide" the implementation from the client code - whenever that may be needed.
This is really about intent - your code is about abstractions - take care of those abstractions, nuture them and protect them, they will serve you well.
The question becomes - which one better represents your intent? I would venture to say that the
FooImpl
is better, I sense your intent is to hide the implementation of the class from the client and this implementation better represents that intent (sinceFooImpl
is not accessible to the client).If your intent to is to use
Bar
elsewhere in the code, outside of the classFoo
, then that implementation is better because that is the intent and that implementation allows you to do that.I doubt that. The implementation of
Bar
andFooImpl
are not visible outside the translation unit they are defined in.Not really, no. In some generic sense, the clearer code is, the easier people are able to scale it.
The PIMPL pattern is typically used to completely hide implementation details from code which uses your class.
For example, your class might be wrapping platform-specific functionality. Even if you used forward declarations and/or conditional compilation, you would end up exposing platform-specific code in your header file. This means that any code using your class would end up with a header-dependency on those types as well, and it means your class would potentially change depending on the platform (e.g. the size might be different).
The PIMPL pattern would let you keep all the platform-specific details hidden away in the implementation file (typically *.cpp or similar). This means no other code anywhere else in the program can directly see it, keeping your wrapper class clean and consistent across all platforms.
That's just one example of where PIMPL comes in handy, but there are other uses for it too.
It's also worth noting that not everything can be forward-declared, and forward declarations often require making undesirable concessions, such as using pointers or references everywhere.