Pimpl idiom vs Pure virtual class interface
Asked Answered
B

10

133

I was wondering what would make a programmer to choose either Pimpl idiom or pure virtual class and inheritance.

I understand that pimpl idiom comes with one explicit extra indirection for each public method and the object creation overhead.

The Pure virtual class in the other hand comes with implicit indirection(vtable) for the inheriting implementation and I understand that no object creation overhead.
EDIT: But you'd need a factory if you create the object from the outside

What makes the pure virtual class less desirable than the pimpl idiom?

Beneficial answered 5/5, 2009 at 14:5 Comment(1)
Great question, just wanted to ask the same thing. See also boost.org/doc/libs/1_41_0/libs/smart_ptr/sp_techniques.htmlErasion
N
60

When writing a C++ class, it's appropriate to think about whether it's going to be

  1. A Value Type

    Copy by value, identity is never important. It's appropriate for it to be a key in a std::map. Example, a "string" class, or a "date" class, or a "complex number" class. To "copy" instances of such a class makes sense.

  2. An Entity type

    Identity is important. Always passed by reference, never by "value". Often, doesn't make sense to "copy" instances of the class at all. When it does make sense, a polymorphic "Clone" method is usually more appropriate. Examples: A Socket class, a Database class, a "policy" class, anything that would be a "closure" in a functional language.

Both pImpl and pure abstract base class are techniques to reduce compile time dependencies.

However, I only ever use pImpl to implement Value types (type 1), and only sometimes when I really want to minimize coupling and compile-time dependencies. Often, it's not worth the bother. As you rightly point out, there's more syntactic overhead because you have to write forwarding methods for all of the public methods. For type 2 classes, I always use a pure abstract base class with associated factory method(s).

Nonreturnable answered 5/5, 2009 at 15:15 Comment(2)
Please see Paul de Vrieze's comment to this answer. Pimpl and Pure Virtual differ significantly if you're in a library and want to swap your .so/.dll without rebuilding the client. Clients link to pimpl frontends by name so keeping old method signatures is enough. OTOH in pure abstract case they effectively link by vtable index so reordering methods or inserting in the middle will break compatibility.Shend
You can only add (or re-order) methods in a Pimpl class front end to maintain binary comparability. Logically speaking, you have still changed the interface and it seems a bit dodgy. The answer here is a sensible balance that may also help with unit testing via "dependency injection"; but the answer always depends on requirements. Third party Library writers (as distinct from using a library in your own organization) may heavily prefer the Pimpl.Nazarite
D
30

Pointer to implementation is usually about hiding structural implementation details. Interfaces are about instancing different implementations. They really serve two different purposes.

Decoy answered 5/5, 2009 at 14:9 Comment(6)
not necessarily, I've seen classes that store multiple pimpls depending on the implementation desired. Often this is say a win32 impl vs a linux impl of something that needs to be implemented differently per platform.Marthmartha
But you can use an Interface to decouple the implementation details and hide themBeneficial
While you can implement pimpl using an interface, there's often no reason to decouple the implementation details. So there's no reason to go polymorphic. The reason for pimpl is to keep implementation details away from the client (in C++ to keep them out of the header). You might do this using an abstract base/interface, but generally that's unnecessary overkill.Partner
Why is it overkill? I mean, is it slower the interface method than the pimpl one? There might be logical reasons, but from the practical point of view I'd say its easier to do it with an abstract interfaceBeneficial
I would say abstract base class/interface is the "normal" way to do things and allows easier testing via mockingElf
This comment gets my upvote because (IMO) it describes exactly the differences. Pimpl solves a specific problem in the best way. That it, hiding implementation details so that it is not necessary to recompile code when the implementation changes. The Qt source code shows lots of examples of this. It allows a Qt application to use a more recent (minor) version of the Qt shared libraries without any recompilation.Insinuate
G
28

The pimpl idiom helps you reduce build dependencies and times especially in large applications, and minimizes header exposure of the implementation details of your class to one compilation unit. The users of your class should not even need to be aware of the existence of a pimple (except as a cryptic pointer to which they are not privy!).

Abstract classes (pure virtuals) is something of which your clients must be aware: if you try to use them to reduce coupling and circular references, you need to add some way of allowing them to create your objects (e.g. through factory methods or classes, dependency injection or other mechanisms).

Grandson answered 5/5, 2009 at 14:12 Comment(0)
C
22

I was searching an answer for the same question. After reading some articles and some practice I prefer using "Pure virtual class interfaces".

  1. They are more straight forward (this is a subjective opinion). Pimpl idiom makes me feel I'm writing code "for the compiler", not for the "next developer" that will read my code.
  2. Some testing frameworks have direct support for Mocking pure virtual classes
  3. It's true that you need a factory to be accessible from the outside. But if you want to leverage polymorphism: that's also "pro", not a "con". ...and a simple factory method does not really hurts so much

The only drawback (I'm trying to investigate on this) is that pimpl idiom could be faster

  1. when the proxy-calls are inlined, while inheriting necessarily need an extra access to the object VTABLE at runtime
  2. the memory footprint the pimpl public-proxy-class is smaller (you can do easily optimizations for faster swaps and other similar optimizations)
Cordoba answered 25/2, 2010 at 0:28 Comment(2)
Also remember that by using inheritance you introduce a dependency on vtable layout. To maintain ABI you cannot change the virtual functions anymore (adding at the end is sort of safe, if the there are no child classes that add virtual methods of their own).Tardigrade
^ This comment here should be a sticky.Dress
M
10

I hate pimples! They do the class ugly and not readable. All methods are redirected to pimple. You never see in headers, what functionalities has the class, so you can not refactor it (e. g. simply change the visibility of a method). The class feels like "pregnant". I think using iterfaces is better and really enough to hide the implementation from the client. You can event let one class implement several interfaces to hold them thin. One should prefer interfaces! Note: You do not necessary need the factory class. Relevant is that the class clients communicate with it's instances via the appropriate interface. The hiding of private methods I find as a strange paranoia and do not see reason for this since we hav interfaces.

Maldonado answered 5/10, 2010 at 14:37 Comment(5)
There are some cases when you can't use pure virtual interfaces. For instance when you have some legacy code and you have two modules which you need to separate without touching them.Laundry
As @Paul de Vrieze pointed out below, you loose ABI compatibility when changing the methods of the base class, because you have an implicit dependence on the vtable of the class. It depends on the use case, whether this is a problem.Excusatory
"The hiding of private methods I find as a strange paranoia" Doesn't that allow you to hide the dependencies and therefore minimize compilation time if a dependency changes?Voracity
I also don't understand how Factories are easier to re-factor than pImpl. Don't you leave the "interface" in both cases and change the implementation? In Factory you have to modify one .h and one .cpp file and in pImpl you have to modify one .h and two .cpp files but that's about it and you generally don't need to modify the cpp file of the pImpl's interface.Voracity
And you very much DO see available functionality in headers, because public member functions still have to be there...?Monstrosity
L
9

There's a very real problem with shared libraries that the pimpl idiom circumvents neatly that pure virtuals can't: you cannot safely modify/remove data members of a class without forcing users of the class to recompile their code. That may be acceptable under some circumstances, but not e.g. for system libraries.

To explain the problem in detail, consider the following code in your shared library/header:

// header
struct A
{
public:
  A();
  // more public interface, some of which uses the int below
private:
  int a;
};

// library 
A::A()
  : a(0)
{}

The compiler emits code in the shared library that calculates the address of the integer to be initialized to be a certain offset (probably zero in this case, because it's the only member) from the pointer to the A object it knows to be this.

On the user side of the code, a new A will first allocate sizeof(A) bytes of memory, then hand a pointer to that memory to the A::A() constructor as this.

If in a later revision of your library you decide to drop the integer, make it larger, smaller, or add members, there'll be a mismatch between the amount of memory user's code allocates, and the offsets the constructor code expects. The likely result is a crash, if you're lucky - if you're less lucky, your software behaves oddly.

By pimpl'ing, you can safely add and remove data members to the inner class, as the memory allocation and constructor call happen in the shared library:

// header
struct A
{
public:
  A();
  // more public interface, all of which delegates to the impl
private:
  void * impl;
};

// library 
A::A()
  : impl(new A_impl())
{}

All you need to do now is keep your public interface free of data members other than the pointer to the implementation object, and you're safe from this class of errors.

Edit: I should maybe add that the only reason I'm talking about the constructor here is that I didn't want to provide more code - the same argumentation applies to all functions that access data members.

Lithic answered 5/5, 2009 at 14:43 Comment(6)
Instead of void *, I think it's more traditional to forward declare the implementing class: class A_impl *impl_;Stinker
I don't understand, you shouldn't declare private members in a virtual pure class you intend to use as an interface, the idea is to keep the class pruely abstract, no size, only pure virtual methods, I don't see anything you can't do through shared librariesBeneficial
@Erasion Krueger: You're right, I was being lazy. @Arkaitz Jimenez: Slight misunderstanding; if you've got a class that only contains pure virtual functions, then there's not much point in talking about shared libraries. On the other hand, if you are dealing with shared libraries, pimpl'ing your public classes can be prudent for the reason outlined above.Lithic
This is just incorrect. Both methods let you hide the implementation state of your classes, if you make your other class a "pure abstract base" class.Nonreturnable
Paul, I fail to see what you think is incorrect about this - care to explain?Lithic
The first sentence in your anser implies that pure virtuals with an associated factory method somehow don't let you hide the internal state of the class. That's not true. Both techniques let you hide the internal state of the class. The difference is how it looks to the user. pImpl allows you to still represent a class with value semantics yet also hide the internal state. Pure Abstract Base Class + factory method allows you to represent entity types, and also lets you hide the internal state. The latter is exactly how COM works. Chapter 1 of "Essential COM" has a great disucssion on this.Nonreturnable
J
6

We must not forget that inheritance is a stronger, closer coupling than delegation. I would also take into account all the issues raised in the answers given when deciding what design idioms to employ in solving a particular problem.

Janayjanaya answered 27/2, 2010 at 12:37 Comment(0)
N
5

Although broadly covered in the other answers maybe I can be a bit more explicit about one benefit of pimpl over virtual base classes:

A pimpl approach is transparent from the user view point, meaning you can e.g. create objects of the class on the stack and use them directly in containers. If you try to hide the implementation using an abstract virtual base class, you will need to return a shared pointer to the base class from a factory, complicating it's use. Consider the following equivalent client code:

// Pimpl
Object pi_obj(10);
std::cout << pi_obj.SomeFun1();

std::vector<Object> objs;
objs.emplace_back(3);
objs.emplace_back(4);
objs.emplace_back(5);
for (auto& o : objs)
    std::cout << o.SomeFun1();

// Abstract Base Class
auto abc_obj = ObjectABC::CreateObject(20);
std::cout << abc_obj->SomeFun1();

std::vector<std::shared_ptr<ObjectABC>> objs2;
objs2.push_back(ObjectABC::CreateObject(13));
objs2.push_back(ObjectABC::CreateObject(14));
objs2.push_back(ObjectABC::CreateObject(15));
for (auto& o : objs2)
    std::cout << o->SomeFun1();
Nesline answered 13/6, 2017 at 14:5 Comment(0)
N
3

In my understanding these two things serve completely different purposes. The purpose of the pimple idiom is basically give you a handle to your implementation so you can do things like fast swaps for a sort.

The purpose of virtual classes is more along the line of allowing polymorphism, i.e. you have a unknown pointer to an object of a derived type and when you call function x you always get the right function for whatever class the base pointer actually points to.

Apples and oranges really.

Nairobi answered 5/5, 2009 at 14:37 Comment(1)
I agree with the apples/oranges. But it appears that you use pImpl for a functional. My goal is mostly build-technical and information hiding.Blameless
L
2

The most annoying problem about the pimpl idiom is it makes it extremely hard to maintain and analyse existing code. So using pimpl you pay with developer time and frustration only to "reduce build dependencies and times and minimize header exposure of the implementation details". Decide yourself, if it is really worth it.

Especially "build times" is a problem you can solve by better hardware or using tools like Incredibuild ( www.incredibuild.com, also already included in Visual Studio 2017 ), thus not affecting your software design. Software design should be generally independent of the way the software is built.

Little answered 18/1, 2018 at 9:27 Comment(6)
You also pay with developer time when build times are 20 minutes instead of 2, so its a bit of a balance, a real module system would help a lot here.Beneficial
IMHO, the way software is built should not influence the internal design at all. This is a completely different problem.Little
What makes it hard to analyze? A bunch of calls in an implementation file forwarding to the Impl class doesn't sound hard.Lockard
Imagine debugging implementations where pimpl and interfaces are both used. Starting from a call in the user code A, you trace to the interface B, jump to pimpled class C to finally start debugging the implementation class D ... Four steps until you can analyse what really happens. And if the whole thing is implemented in a DLL you will possilbly find a C interface somewhere inbetween... .Little
Why would you use an interface with pImpl when pImpl can also do the job of an interface? (i.e. it can help you achieve dependency inversion)Voracity
I cannot ask this question to the guy who did it 10 years ago ;-)Little

© 2022 - 2024 — McMap. All rights reserved.