Reputation: 9035
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
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.
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
Reputation: 206577
This is, obviously, a matter of opinion.
I think using the second approach goes against the spirit of the Pimpl idiom.
Example
, it will most likely affect four files instead of two.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