Search code examples
c++oopoptimizationdependency-managementpimpl-idiom

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?


Solution

  • 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.