pimpl: Avoiding pointer to pointer with pimpl
In this question I asked "pimpl: shared_ptr or unique_ptr" I've been convinced that the proper usage of the pimpl idiom is to use a unique_ptr
, not a shared_ptr
. It should act to the user as if there is no pointer at all, whereas quite clearly the shared_ptr
introduces aliasing upon copying, which definitely acts like a pointer.
So, lets say a user wants to create a shared_ptr
to my pimpl object (say if they want actually want multiple aliases to it). For example:
shared_ptr<my_pimpl> p(new my_pimpl());
That would result in a shared_ptr
pointing to a unique_ptr
pointing to my implementation.
开发者_运维百科It would be nice if I could achieve something like the following:
my_pimpl x; // (1)
shared_ptr<my_pimpl> p(new my_pimpl()); // (2) Pointer to pointer here.
x.f(); // (3)
p->f(); // (4)
but with somehow getting rid of the pointer to pointer, whilst still maintaining the implementation hiding of pimpl.
Any ideas how to achieve this (I'm happy to change the line (2) and obviously my_pimpl
, but want lines (3) and (4) to stay the same).
There are a number of possible approaches depending on your constraints.
1. Create your own shared_my_pimpl class
Create a class shared_my_pimpl
which has the same interface as my_pimpl
but internally uses a shared_ptr instead of a unique_ptr
. Now create a class shared_ptr_my_pimpl
which holds a shared_my_pimpl
and has an operator->
which returns a pointer to the shared_my_pimpl
, so that you get ->
notation instead of .
notation for member access. You can add a function make_shared_ptr_my_pimpl
to make it look more like shared_ptr
usage.
Disadvantages:
- The type of the object is not
shared_ptr<x>
butshared_ptr_my_pimpl
; it's just pretending to be ashared_ptr
. - You can't get a
my_pimpl*
ormy_pimpl&
to the object; it's a different type which just behaves the same.
2. Derive from an interface
Create an interface my_pimpl_interface
with all relevant functions pure virtual. Derive both my_pimpl
and my_pimpl::impl
(your pimpl implementation class) from this interface. Add a function make_shared_my_pimpl
which returns a shared_ptr<my_pimpl_interface>
to a my_pimpl::impl
. You can now refer to both the plain object and the shared_ptr object as my_pimpl_interface&
.
Disadvantages:
- By making all functions virtual you incur an extra indirection in calling them, which may have been what you were trying to avoid. Your standard
my_pimpl
object will also pay this overhead.
You should use an interface for this purpose, because then the user of your class can choose whether they want to use shared_ptr
or unique_ptr
.
精彩评论