Wes McKinney <[email protected]> writes:
> The abstract/all-virtual base has some benefits:
>
> * No need to implement "forwarding" methods to the private implementation
> * Do not have to declare "friend" classes in the header for some cases
> where other classes need to access the methods of a private
> implementation
> * Implementation symbols do not need to be exported in DLLs with an
> *_EXPORT macro
>
> There are some drawbacks, or cases where this method cannot be applied,
> though:
>
> * An implementation of some other abstract interface which needs to
> appear in a public header may not be able to use this approach.
> * My understanding is that the PIMPL pattern will perform better for
> non-virtual functions that are called a lot. It'd be helpful to know
> the magnitude of the performance difference
> * Complex inheritance patterns may require use of virtual inheritance,
> which can create a burden for downstream users (e.g. they may have to
> use dynamic_cast to convert between types in the class hierarchy)
I would add these two points, which may or may not be a significant
concern to you:
* When you add new methods to the abstract virtual model, you change the
ABI [1] and therefore need to recompile client code. This has many
consequences for distribution.
* PIMPL gives a well-defined place for input validation and setting
debugger breakpoints even when you don't know which implementation
will be used.
[1] The ABI changes because code to index into the vtable is inlined at
the call site. Adding to your example
void foo(VirtualType &obj) {
obj.Method1();
// any other line to suppress tail call
}
produces assembly like
mov rax,QWORD PTR [rdi] ; load vtable for obj
call QWORD PTR [rax+0x10] ; 0x10 is offset into vtable
A different method will use a different offset. If you add a method,
offsets of existing methods may change. With PIMPL, the equivalent
indexing code resides in your library instead of client code, and yields
a static (or PLT) call resolved by the linker:
call _ZN9PIMPLType7Method1Ev@PLT