I want to use pimpl idiom with inheritance.
Here is the base public class and its implementation class:
class A
{
public:
A(){pAImpl = new AImpl;};
void foo(){pAImpl->foo();};
private:
AImpl* pAImpl;
};
class AImpl
{
public:
void foo(){/*do something*/};
};
And I want to be able to create the derived public class with its implementation class:
class B : public A
{
public:
void bar(){pAImpl->bar();}; // Can't do! pAimpl is A's private.
};
class BImpl : public AImpl
{
public:
void bar(){/*do something else*/};
};
But I can't use pAimpl in B because it is A's private.
So I see some ways to solve it:
- Create BImpl* pBImpl member in B, and pass it to A with additional A constructor, A(AImpl*).
- Change pAImpl to be protected (or add a Get function), and use it in B.
- B shouldn't inherit from A. Create BImpl* pBImpl member in B, and create foo() and bar() in B, that will use pBImpl.
- Any other way?
What should I choose?
The correct way is to do (2).
In general, you should probably consider to make all you member variables protected by default instead of private.
The reason most programmers choose private is that they don't think about others who want to derive from their class and most introductory C++ manuals teach this style, in the sense that all the examples use private.
EDIT
Code duplication and memory allocation are undesired side-effects of using the pimp design pattern and cannot be avoided to my knowledge.
If you need to have Bimpl inherit Aimpl and you want to expose a consistent interface to them through A and B, B would also need to inherit A.
One thing you can do to simplify things in this scenario is to have B inherit from A and only change the contructor such that B::B(...) {} creates a Bimpl, and add dispatches for all methods of Bimpl that are not in Aimpl.
I think the best way from a purely object-oriented-theoretical perspective is to not make BImpl inherit from AImpl (is that what you meant in option 3?). However, having BImpl derive from AImpl (and passing the desired impl to a constructor of A) is OK as well, provided that the pimpl member variable is
const
. It doesn't really matter whether you use a get functions or directly access the variable from derived classes, unless you want to enforce const-correctness on the derived classes. Letting derived classes change pimpl isn't a good idea - they could wreck all of A's initialisation - and nor is letting the base class change it a good idea. Consider this extension to your example:Although you might not have a swap() function, you can easily conceive of similar problems occurring, particularly if A is assignable, whether by accident or intention. It's a somewhat subtle violation of the Liskov substitutability principle. The solutions are to either:
Don't change the pimpl members after construction. Declare them to be
AImpl * const pimpl
. Then, the derived constructors can pass an appropriate type and the rest of the derived class can downcast confidently. However, then you can't e.g. do non-throwing swaps, assignments, or copy-on-write, because these techniques require that you can change the pimpl member. However however, you're probably not really intending to do these things if you have an inheritance hierarchy.Have unrelated (and dumb) AImpl and BImpl classes for A and B's private variables, respectively. If B wants to do something to A, then use A's public or protected interface. This also preserves the most common reason to using pimpl: being able to hide the definition of AImpl away in a cpp file that derived classes can't use, so half your program doesn't need to recompile when A's implementation changes.
I would do (1) because A's privates are or no business for B.
Actually I would not pass it to A as you suggest, because A makes its own in A::A(). Calling
pApimpl->whatever()
from Bis also not appropriate (private means private).As stefan.ciobaca said, if you really wanted A to be extendable, you'd want
pAImpl
to be protected.However, your definition in
B
ofvoid bar(){pAImpl->bar();};
seems odd, asbar
is a method onBImpl
and notAImpl
.There are at least three easy alternatives that would avoid that issue:
BImpl
extendsAImpl
(inheriting the existing implementation offoo
rather than defining another),BImpl
definesbar
, andB
uses its privateBImpl* pBImpl
to access both.B
holds private pointers to each ofAImpl
andBImpl
and forwards each offoo
andbar
to the appropriate implementer.