views:

842

answers:

5

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:

  1. Create BImpl* pBImpl member in B, and pass it to A with additional A constructor, A(AImpl*).
  2. Change pAImpl to be protected (or add a Get function), and use it in B.
  3. B shouldn't inherit from A. Create BImpl* pBImpl member in B, and create foo() and bar() in B, that will use pBImpl.
  4. Any other way?

What should I choose?

A: 

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).

EricSchaefer
If I create A(AImpl*), it will receive Aimpl* from B, and will not create its own.
Igor Oks
I know that. But A and B should really have their own privates. Thats exactly why they are called 'private'. If B inherits from A without PIMPL B cannot see and use A's privates either, so why should that be any different with PIMPL?
EricSchaefer
+1  A: 

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.

yeah i would go for protected too. but how do you deal with creation of the pimpl? should each class have its own pimpl? or should they share all a same pimpl that resides in the base and which is created by the most derived class, put forward as constructor arguments?
Johannes Schaub - litb
without thinking about it, i would probably go with a separate pimpl for each derived class. but that requires one dynamic memory allocation for each. could not be desired. but it would probably be the easiest thing to do. not sure how to deal with virtuals in the pimpls though.
Johannes Schaub - litb
+1  A: 

As stefan.ciobaca said, if you really wanted A to be extendable, you'd want pAImpl to be protected.

However, your definition in B of void bar(){pAImpl->bar();}; seems odd, as bar is a method on BImpl and not AImpl.

There are at least three easy alternatives that would avoid that issue:

  1. Your alternative (3).
  2. A variation on (3) in which BImpl extends AImpl (inheriting the existing implementation of foo rather than defining another), BImpl defines bar, and B uses its private BImpl* pBImpl to access both.
  3. Delegation, in which B holds private pointers to each of AImpl and BImpl and forwards each of foo and bar to the appropriate implementer.
joel.neely
+2  A: 
Doug
A: 
class A
{
    public:
      A(bool DoNew = true){
        if(DoNew)
          pAImpl = new AImpl;
      };
      void foo(){pAImpl->foo();};
    protected:
      void SetpAImpl(AImpl* pImpl) {pAImpl = pImpl;};
    private:
      AImpl* pAImpl;  
};
class AImpl
{
    public:
      void foo(){/*do something*/};
};

class B : public A
{
    public:
      B() : A(false){
          pBImpl = new BImpl;
          SetpAImpl(pBImpl);
      };
      void bar(){pBImpl->bar();};    
    private:
      BImpl* pBImpl;  
};        

class BImpl : public AImpl
{
    public:
      void bar(){/*do something else*/};
};
Igor Oks