tjwrona
tjwrona

Reputation: 9035

Using the PIMPL idiom, should the implementation always be a private member of the class?

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?

Upvotes: 1

Views: 869

Answers (2)

tjwrona
tjwrona

Reputation: 9035

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!

Upvotes: 0

R Sahu
R Sahu

Reputation: 206577

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.

Upvotes: 6

Related Questions