1

I've seen the PIMPL idiom implemented in two different ways. One approach is to always make the implementation a private member of the class. This ensures that the implementation cannot be accessed from outside the class no matter what.

For example:

Example.h

#ifndef EXAMPLE_H
#define EXAMPLE_H

#include <memory>

class Example {
public:
    void doSomething();
private:
    struct Impl;
    std::shared_ptr<Impl> pimpl_;
};

#endif /* EXAMPLE_H */

Example.cpp

#include "Example.h"
#include <iostream>

struct Example::Impl {
    void doSomething() {
        std::cout << "Hello World!" << std::endl;
    }
};

void Example::doSomething() {
    pimpl_->doSomething();
}

main.cpp

#include "Example.h"

int main() {
    Example ex;
    ex.doSomething();
    system("pause");
}

The other approach I have seen, is to make the implementation a completely separate class with it's own .h file and its own .cpp file.

For example:

Example.h

#ifndef EXAMPLE_H
#define EXAMPLE_H

#include <memory>

struct ExampleImpl;

class Example {
public:
    void doSomething();
private:
    std::shared_ptr<ExampleImpl> pimpl_;
};

#endif /* EXAMPLE_H */

ExampleImpl.h

#ifndef EXAMPLE_IMPL_H
#define EXAMPLE_IMPL_H

struct ExampleImpl {
public:
    void doSomething();
};

#endif /* EXAMPLE_IMPL_H */

Example.cpp

#include "Example.h"
#include "ExampleImpl.h"
#include <iostream>

void Example::doSomething() {
    pimpl_->doSomething();
}

ExampleImpl.cpp

#include "ExampleImpl.h"
#include <iostream>

void ExampleImpl::doSomething() {
    std::cout << "Hello World!" << std::endl;
}

main.cpp

#include "Example.h"

int main() {
    Example ex;
    ex.doSomething();
    system("pause");
}

The only notable difference I can see is using the second approach you can access the implementation without going through the Example class. I don't personally see any reason why anyone would need to do this though.

Both approaches seem to work and satisfy the end goal, but are there any real advantages for choosing one approach over the other?

tjwrona1992
  • 8,614
  • 8
  • 35
  • 98
  • 1
    Maybe testability? – Kerrek SB Jul 31 '17 at 16:46
  • I guess, but if every call on the initial class is just delegated to the implementation shouldn't you be able to test all of your code by going through the initial class without needing to access the implementation directly? – tjwrona1992 Jul 31 '17 at 16:48
  • 1
    @KerrekSB for testing purposes you should be interested in the *behavior* of the class, implementation details should be irrelevant. In the ideal case of course - if you have a counter example it would be good to know about. – Mark Ransom Jul 31 '17 at 16:53
  • If you want to derive from Example, the second method is a lot easier to work with. I worked at a place where we did it and think it's the better way. – QuestionC Jul 31 '17 at 17:21
  • @QuestionC, could you elaborate on how it makes it easier to derive from `Example`? – tjwrona1992 Jul 31 '17 at 17:33
  • If you want to make a second library based off the first, you're going to want to derive from the implementation class. Can't do that in the first method. – QuestionC Jul 31 '17 at 17:42
  • @QuestionC I don't see why you would want to derive from the implementation when you can just derive from `Example` and your derived class will have all of the same functionality. – tjwrona1992 Jul 31 '17 at 17:45
  • The second approach has a big drawback: 2 more files to manage. Having a **.h** and a **.cpp** is already enough to manage for implementing a single "component". Having to manage a **.h**, a **.cpp**, a **impl.h**, *and* a **impl.cpp** is just so much more work. – Justin Jul 31 '17 at 18:04
  • @MarkRansom: That's certainly a reasonable position, but I wouldn't be entirely opposed to a situation where private implementation code has its own, private tests. It all depends. Of course you could ask for a more absolute line on testing only public interfaces and composing everything from public parts, but that may not always be the pragmatic route. – Kerrek SB Jul 31 '17 at 21:51

2 Answers2

6

This is, obviously, a matter of opinion.

I think using the second approach goes against the spirit of the Pimpl idiom.

  1. The details of the implementation are now visible to the outside.
  2. If any changes are made to the interface of Example, it will most likely affect four files instead of two.
  3. If any changes are made to the way ExampleImpl is implemented, it will most likely affect three files instead of one.

Given the above points, I would recommend using the nested class approach.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

I have actually found a situation where the second approach is the clear winner. You can run into issues with inheritance if you need to derive from a class that is using the PIMPL idiom to create a derived class that is also using the PIMPL idiom.

If the implementation class of the derived class does not inherit from the implementation class of the base class you will be unable to use any inherited functions in the implementation of the derived class which can be a total show stopper!

Unless someone can find a simple way around this I will be forced to use the second approach.

EDIT:

It turns out that there is a solution to this issue without going to the second approach! You can simply make the implementation class for the derived class inherit from the NON implementation class of the base class! That gives you all of the functionality with none of the hassle!

tjwrona1992
  • 8,614
  • 8
  • 35
  • 98