How to use the Qt's PIMPL idiom?
Introduction
The PIMPL is a private class that contains all of the implementation-specific data of the parent class. Qt provides a PIMPL framework and a set of conventions that need to be followed when using that framework. Qt's PIMPLs can be used in all classes, even those not derived from QObject
.
The PIMPL needs to be allocated on the heap. In idiomatic C++, we must not manage such storage manually, but use a smart pointer. Either QScopedPointer
or std::unique_ptr
work for this purpose. Thus, a minimal pimpl-based interface, not derived from QObject
, might look like:
// Foo.h
#include <QScopedPointer>
class FooPrivate; ///< The PIMPL class for Foo
class Foo {
QScopedPointer<FooPrivate> const d_ptr;
public:
Foo();
~Foo();
};
The destructor's declaration is necessary, since the scoped pointer's destructor needs to destruct an instance of the PIMPL. The destructor must be generated in the implementation file, where the FooPrivate
class lives:
// Foo.cpp
class FooPrivate { };
Foo::Foo() : d_ptr(new FooPrivate) {}
Foo::~Foo() {}
See also:
- A deeper exposition of the idiom.
- Gotchas and pitfalls of PIMPL.
The Interface
We'll now explain the PIMPL-based CoordinateDialog
interface in the question.
Qt provides several macros and implementation helpers that reduce the drudgery of PIMPLs. The implementation expects us to follow these rules:
- The PIMPL for a class
Foo
is namedFooPrivate
. - The PIMPL is forward-declared along the declaration of the
Foo
class in the interface (header) file.
The Q_DECLARE_PRIVATE Macro
The Q_DECLARE_PRIVATE
macro must be put in the private
section of the class's declaration. It takes the interface class's name as a parameter. It declares two inline implementations of the d_func()
helper method. That method returns the PIMPL pointer with proper constness. When used in const methods, it returns a pointer to a const PIMPL. In non-const methods, it returns a pointer to a non-const PIMPL. It also provides a pimpl of correct type in derived classes. It follows that all access to the pimpl from within the implementation is to be done using d_func()
and **not through d_ptr
. Usually we'd use the Q_D
macro, described in the Implementation section below.
The macro comes in two flavors:
Q_DECLARE_PRIVATE(Class) // assumes that the PIMPL pointer is named d_ptr
Q_DECLARE_PRIVATE_D(Dptr, Class) // takes the PIMPL pointer name explicitly
In our case, Q_DECLARE_PRIVATE(CoordinateDialog)
is equivalent to Q_DECLARE_PRIVATE_D(d_ptr, CoordinateDialog)
.
The Q_PRIVATE_SLOT Macro
This macro is only needed for Qt 4 compatibility, or when targeting non-C++11 compilers. For Qt 5, C++11 code, it is unnecessary, as we can connect functors to signals and there's no need for explicit private slots.
We sometimes need for a QObject
to have private slots for internal use. Such slots would pollute the interface's private section. Since the information about slots is only relevant to the moc code generator, we can, instead, use the Q_PRIVATE_SLOT
macro to tell moc that a given slot is to be invoked through the d_func()
pointer, instead of through this
.
The syntax expected by moc in the Q_PRIVATE_SLOT
is:
Q_PRIVATE_SLOT(instance_pointer, method signature)
In our case:
Q_PRIVATE_SLOT(d_func(), void onAccepted())
This effectively declares an onAccepted
slot on the CoordinateDialog
class. The moc generates the following code to invoke the slot:
d_func()->onAccepted()
The macro itself has an empty expansion - it only provides information to moc.
Our interface class is thus expanded as follows:
class CoordinateDialog : public QDialog
{
Q_OBJECT /* We don't expand it here as it's off-topic. */
// Q_DECLARE_PRIVATE(CoordinateDialog)
inline CoordinateDialogPrivate* d_func() {
return reinterpret_cast<CoordinateDialogPrivate *>(qGetPtrHelper(d_ptr));
}
inline const CoordinateDialogPrivate* d_func() const {
return reinterpret_cast<const CoordinateDialogPrivate *>(qGetPtrHelper(d_ptr));
}
friend class CoordinateDialogPrivate;
// Q_PRIVATE_SLOT(d_func(), void onAccepted())
// (empty)
QScopedPointer<CoordinateDialogPrivate> const d_ptr;
public:
[...]
};
When using this macro, you must include the moc-generated code in a place where the private class is fully defined. In our case, this means that the CoordinateDialog.cpp
file should end with:
#include "moc_CoordinateDialog.cpp"
Gotchas
All of the
Q_
macros that are to be used in a class declaration already include a semicolon. No explicit semicolons are needed afterQ_
:// correct // verbose, has double semicolons
class Foo : public QObject { class Foo : public QObject {
Q_OBJECT Q_OBJECT;
Q_DECLARE_PRIVATE(...) Q_DECLARE_PRIVATE(...);
... ...
}; };The PIMPL must not be a private class within
Foo
itself:// correct // wrong
class FooPrivate; class Foo {
class Foo { class FooPrivate;
... ...
}; };The first section after the opening brace in a class declaration is private by default. Thus the following are equivalent:
// less wordy, preferred // verbose
class Foo { class Foo {
int privateMember; private:
int privateMember;
}; };The
Q_DECLARE_PRIVATE
expects the interface class's name, not the PIMPL's name:// correct // wrong
class Foo { class Foo {
Q_DECLARE_PRIVATE(Foo) Q_DECLARE_PRIVATE(FooPrivate)
... ...
}; };The PIMPL pointer should be const for non-copyable/non-assignable classes such as
QObject
. It can be non-const when implementing copyable classes.Since the PIMPL is an internal implementation detail, its size is not available at the site where the interface is used. The temptation to use placement new and the Fast Pimpl idiom should be resisted as it provides no benefits for anything but a class that doesn't allocate memory at all.
The Implementation
The PIMPL has to be defined in the implementation file. If it is large, it can also be defined in a private header, customarily named foo_p.h
for a class whose interface is in foo.h
.
The PIMPL, at a minimum, is merely a carrier of the main class's data. It only needs a constructor and no other methods. In our case, it also needs to store the pointer to the main class, as we'll want to emit a signal from the main class. Thus:
// CordinateDialog.cpp
#include <QFormLayout>
#include <QDoubleSpinBox>
#include <QDialogButtonBox>
class CoordinateDialogPrivate {
Q_DISABLE_COPY(CoordinateDialogPrivate)
Q_DECLARE_PUBLIC(CoordinateDialog)
CoordinateDialog * const q_ptr;
QFormLayout layout;
QDoubleSpinBox x, y, z;
QDialogButtonBox buttons;
QVector3D coordinates;
void onAccepted();
CoordinateDialogPrivate(CoordinateDialog*);
};
The PIMPL is not copyable. Since we use non-copyable members, any attempt to copy or assign to the PIMPL would be caught by the compiler. Generally, it's best to explicitly disable the copy functionality by using Q_DISABLE_COPY
.
The Q_DECLARE_PUBLIC
macro works similarly to Q_DECLARE_PRIVATE
. It is described later in this section.
We pass the pointer to the dialog into the constructor, allowing us to initialize the layout on the dialog. We also connect the QDialog
's accepted signal to the internal onAccepted
slot.
CoordinateDialogPrivate::CoordinateDialogPrivate(CoordinateDialog * dialog) :
q_ptr(dialog),
layout(dialog),
buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
{
layout.addRow("X", &x);
layout.addRow("Y", &y);
layout.addRow("Z", &z);
layout.addRow(&buttons);
dialog->connect(&buttons, SIGNAL(accepted()), SLOT(accept()));
dialog->connect(&buttons, SIGNAL(rejected()), SLOT(reject()));
#if QT_VERSION <= QT_VERSION_CHECK(5,0,0)
this->connect(dialog, SIGNAL(accepted()), SLOT(onAccepted()));
#else
QObject::connect(dialog, &QDialog::accepted, [this]{ onAccepted(); });
#endif
}
The onAccepted()
PIMPL method needs to be exposed as a slot in Qt 4/non-C++11 projects. For Qt 5 and C++11, this is no longer necessary.
Upon the acceptance of the dialog, we capture the coordinates and emit the acceptedCoordinates
signal. That's why we need the public pointer:
void CoordinateDialogPrivate::onAccepted() {
Q_Q(CoordinateDialog);
coordinates.setX(x.value());
coordinates.setY(y.value());
coordinates.setZ(z.value());
emit q->acceptedCoordinates(coordinates);
}
The Q_Q
macro declares a local CoordinateDialog * const q
variable. It is described later in this section.
The public part of the implementation constructs the PIMPL and exposes its properties:
CoordinateDialog::CoordinateDialog(QWidget * parent, Qt::WindowFlags flags) :
QDialog(parent, flags),
d_ptr(new CoordinateDialogPrivate(this))
{}
QVector3D CoordinateDialog::coordinates() const {
Q_D(const CoordinateDialog);
return d->coordinates;
}
CoordinateDialog::~CoordinateDialog() {}
The Q_D
macro declares a local CoordinateDialogPrivate * const d
variable. It is described below.
The Q_D Macro
To access the PIMPL in an interface method, we can use the Q_D
macro, passing it the name of the interface class.
void Class::foo() /* non-const */ {
Q_D(Class); /* needs a semicolon! */
// expands to
ClassPrivate * const d = d_func();
...
To access the PIMPL in a const interface method, we need to prepend the class name with the const
keyword:
void Class::bar() const {
Q_D(const Class);
// expands to
const ClassPrivate * const d = d_func();
...
The Q_Q Macro
To access the interface instance from a non-const PIMPL method, we can use the Q_Q
macro, passing it the name of the interface class.
void ClassPrivate::foo() /* non-const*/ {
Q_Q(Class); /* needs a semicolon! */
// expands to
Class * const q = q_func();
...
To access the interface instance in a const PIMPL method, we prepend the class name with the const
keyword, just as we did for the Q_D
macro:
void ClassPrivate::foo() const {
Q_Q(const Class); /* needs a semicolon! */
// expands to
const Class * const q = q_func();
...
The Q_DECLARE_PUBLIC Macro
This macro is optional and is used to allow access to the interface from the PIMPL. It is typically used if the PIMPL's methods need to manipulate the interface's base class, or emit its signals. The equivalent Q_DECLARE_PRIVATE
macro was used to allow access to the PIMPL from the interface.
The macro takes the interface class's name as a parameter. It declares two inline implementations of the q_func()
helper method. That method returns the interface pointer with proper constness. When used in const methods, it returns a pointer to a const interface. In non-const methods, it returns a pointer to a non-const interface. It also provides the interface of correct type in derived classes. It follows that all access to the interface from within the PIMPL is to be done using q_func()
and **not through q_ptr
. Usually we'd use the Q_Q
macro, described above.
The macro expects the pointer to the interface to be named q_ptr
. There is no two-argument variant of this macro that would allow to choose a different name for the interface pointer (as was the case for Q_DECLARE_PRIVATE
).
The macro expands as follows:
class CoordinateDialogPrivate {
//Q_DECLARE_PUBLIC(CoordinateDialog)
inline CoordinateDialog* q_func() {
return static_cast<CoordinateDialog*>(q_ptr);
}
inline const CoordinateDialog* q_func() const {
return static_cast<const CoordinateDialog*>(q_ptr);
}
friend class CoordinateDialog;
//
CoordinateDialog * const q_ptr;
...
};
The Q_DISABLE_COPY Macro
This macro deletes the copy constructor and the assignment operator. It must appear in the private section of the PIMPL.
Common Gotchas
The interface header for a given class must be the first header to be included in the implementation file. This forces the header to be self-contained and not dependent on declarations that happen to be included in the implementation. If it isn't so, the implementation will fail to compile, allowing you to fix the interface to make it self-sufficient.
// correct // error prone
// Foo.cpp // Foo.cpp
#include "Foo.h" #include <SomethingElse>
#include <SomethingElse> #include "Foo.h"
// Now "Foo.h" can depend on SomethingElse without
// us being aware of the fact.The
Q_DISABLE_COPY
macro must appear in the private section of the PIMPL// correct // wrong
// Foo.cpp // Foo.cpp
class FooPrivate { class FooPrivate {
Q_DISABLE_COPY(FooPrivate) public:
... Q_DISABLE_COPY(FooPrivate)
}; ...
};
PIMPL And Non-QObject Copyable Classes
The PIMPL idiom allows one to implement copyable, copy- and move- constructible, assignable object. The assignment is done through the copy-and-swap idiom, preventing code duplication. The PIMPL pointer must not be const, of course.
In C++11, we need to heed the Rule of Four, and provide all of the following: the copy constructor, move constructor, assignment operator, and destructor. And the free-standing swap
function to implement it all, of course†.
We'll illustrate this using a rather useless, but nevertheless correct example.
Interface
// Integer.h
#include <algorithm>
#include <QScopedPointer>
class IntegerPrivate;
class Integer {
Q_DECLARE_PRIVATE(Integer)
QScopedPointer<IntegerPrivate> d_ptr;
public:
Integer();
Integer(int);
Integer(const Integer & other);
Integer(Integer && other);
operator int&();
operator int() const;
Integer & operator=(Integer other);
friend void swap(Integer& first, Integer& second) /* nothrow */;
~Integer();
};
For performance, the move constructor and the assignment operator should be defined in the interface (header) file. They don't need to access the PIMPL directly:
Integer::Integer(Integer && other) : Integer() {
swap(*this, other);
}
Integer & Integer::operator=(Integer other) {
swap(*this, other);
return *this;
}
All of those use the swap
freestanding function, which we must define in the interface as well. Note that it is
void swap(Integer& first, Integer& second) /* nothrow */ {
using std::swap;
swap(first.d_ptr, second.d_ptr);
}
Implementation
This is rather straightforward. We don't need access to the interface from the PIMPL, thus Q_DECLARE_PUBLIC
and q_ptr
are absent.
// Integer.cpp
#include "Integer.h"
class IntegerPrivate {
public:
int value;
IntegerPrivate(int i) : value(i) {}
};
Integer::Integer() : d_ptr(new IntegerPrivate(0)) {}
Integer::Integer(int i) : d_ptr(new IntegerPrivate(i)) {}
Integer::Integer(const Integer &other) :
d_ptr(new IntegerPrivate(other.d_func()->value)) {}
Integer::operator int&() { return d_func()->value; }
Integer::operator int() const { return d_func()->value; }
Integer::~Integer() {}
†Per this excellent answer: There are other claims that we should specialize std::swap
for our type, provide an in-class swap
along-side a free-function swap
, etc. But this is all unnecessary: any proper use of swap
will be through an unqualified call, and our function will be found through ADL. One function will do.
Pimpl idiom usage in Qt, searching for laconic way
Introduction
I know about Q_DECLARE_PRIVATE, Q_D and other macros
You know about them, but have you actually used them and understand their purpose, and - for the most part - their inevitability? Those macros weren't added to make stuff verbose. They are there because you end up needing them.
There are no differences in Qt PIMPL implementation between Qt versions, but you are depending on Qt's implementation details when you inherit from QClassPrivate
, should you do so. The PIMPL macros have nothing to do with moc. You can use them in plain C++ code that doesn't use any Qt classes at all.
Alas, there's no escaping what you want for as long as you implement the PIMPLs the usual way (which is also Qt way).
Pimpl-pointer vs this
First of all, let's observe that impl
stands for this
, but the language lets you skip using this->
in most cases. Thus, it's nothing too foreign.
class MyClassNoPimpl {
int foo;
public:
void setFoo(int s) { this->foo = s; }
};
class MyClass {
struct MyClassPrivate;
QScopedPointer<MyClassPrivate> const d;
public:
void setFoo(int s);
...
virtual ~MyClass();
};
void MyClass::setFoo(int s) { d->foo = s; }
Inheritance demands...
Things become generally outlandish when you have inheritance, though:
class MyDerived : public MyClass {
class MyDerivedPrivate;
QScopedPointer<MyDerivedPrivate> const d;
public:
void SetBar(int s);
};
void MyDerived::setFooBar(int f, int b) {
MyClass::d->foo = f;
d->bar = b;
}
You'll want to re-use a single d-pointer in the base class, but it will have the wrong type in all derived classes. Thus you might think of casting it - that's even more boilerplate! Instead, you use a private function that returns a correctly-cast d-pointer. Now you need to derive both public and private classes, and you need private headers for the private classes, so that the derived classes can use them. Oh, and you need to pass the pointer to the derived pimpl to the base class - because that's the only way you can initialize the d_ptr
while keeping it const, as it must be. See - Qt's PIMPL implementation is verbose because you do actually need all of it to write safe, composable, maintainable code. No way around it.
MyClass1.h
class MyClass1 {
protected:
struct Private;
QScopedPointer<Private> const d_ptr;
MyClass1(Private &); // a signature that won't clash with anything else
private:
inline Private *d() { return (Private*)d_ptr; }
inline const Private *d() const { return (const Private*)d_ptr; }
public:
MyClass1();
virtual ~MyClass1();
void setFoo(int);
};
MyClass1_p.h
struct MyClass1::Private {
int foo;
};
MyClass1.cpp
#include "MyClass1.h"
#include "MyClass1_p.h"
MyClass1::MyClass1(Private &p) : d_ptr(&p) {}
MyClass1::MyClass1() : d_ptr(new Private) {}
MyClass1::~MyClass1() {} // compiler-generated
void MyClass1::setFoo(int f) {
d()->foo = f;
}
MyClass2.h
#include "MyClass1.h"
class MyClass2 : public MyClass1 {
protected:
struct Private;
private:
inline Private *d() { return (Private*)d_ptr; }
inline const Private *d() { return (const Private*)d_ptr; }
public:
MyClass2();
~MyClass2() override; // Override ensures that the base had a virtual destructor.
// The virtual keyword is not used per DRY: override implies it.
void setFooBar(int, int);
};
MyClass2_p.h
#include "MyClass1_p.h"
struct MyClass2::Private : MyClass1::Private {
int bar;
};
MyClass2.cpp
MyClass2::MyClass2() : MyClass1(*new Private) {}
MyClass2::~MyClass2() {}
void MyClass2::setFooBar(int f, int b) {
d()->foo = f;
d()->bar = b;
}
Inheritance, Qt way
Qt's PIMPL macros take care of implementing d()
functions. Well, they implement d_func()
and then you use the Q_D
macro to obtain a local variable that is simply d
. Rewriting the above:
MyClass1.h
class MyClass1Private;
class MyClass1 {
Q_DECLARE_PRIVATE(MyClass1)
protected:
QScopedPointer<Private> d_ptr;
MyClass1(MyClass1Private &);
public:
MyClass1();
virtual ~MyClass1();
void setFoo(int);
};
MyClass1_p.h
struct MyClass1Private {
int foo;
};
MyClass1.cpp
#include "MyClass1.h"
#include "MyClass1_p.h"
MyClass1::MyClass1(MyClass1Private &d) : d_ptr(*d) {}
MyClass1::MyClass1() : d_ptr(new MyClass1Private) {}
MyClass1::MyClass1() {}
void MyClass1::setFoo(int f) {
Q_D(MyClass1);
d->foo = f;
}
MyClass2.h
#include "MyClass1.h"
class MyClass2Private;
class MyClass2 : public MyClass1 {
Q_DECLARE_PRIVATE(MyClass2)
public:
MyClass2();
~MyClass2() override;
void setFooBar(int, int);
};
MyClass2_p.h
#include "MyClass1_p.h"
struct MyClass2Private : MyClass1Private {
int bar;
};
MyClass2.cpp
MyClass2() : MyClass1(*new MyClass2Private) {}
MyClass2::~MyClass2() {}
void MyClass2::setFooBar(int f, int b) {
Q_D(MyClass2);
d->foo = f;
d->bar = b;
}
Factories simplify pimpl
For class hierarchies that are sealed (i.e. where the user doesn't derive), the interface can be sanitized from any private details whatsoever by the use of factories:
Interfaces
class MyClass1 {
public:
static MyClass1 *make();
virtual ~MyClass1() {}
void setFoo(int);
};
class MyClass2 : public MyClass1 {
public:
static MyClass2 *make();
void setFooBar(int, int);
};
class MyClass3 : public MyClass2 {
public:
static MyClass3 *make();
void setFooBarBaz(int, int, int);
};
Implementations
template <class R, class C1, class C2, class ...Args, class ...Args2>
R impl(C1 *c, R (C2::*m)(Args...args), Args2 &&...args) {
return (*static_cast<C2*>(c).*m)(std::forward<Args2>(args)...);
}
struct MyClass1Impl {
int foo;
};
struct MyClass2Impl : MyClass1Impl {
int bar;
};
struct MyClass3Impl : MyClass2Impl {
int baz;
};
struct MyClass1X : MyClass1, MyClass1Impl {
void setFoo(int f) { foo = f; }
};
struct MyClass2X : MyClass2, MyClass2Impl {
void setFooBar(int f, int b) { foo = f; bar = b; }
};
struct MyClass3X : MyClass3, MyClass3Impl {
void setFooBarBaz(int f, int b, int z) { foo = f; bar = b; baz = z;}
};
MyClass1 *MyClass1::make() { return new MyClass1X; }
MyClass2 *MyClass2::make() { return new MyClass2X; }
MyClass3 *MyClass3::make() { return new MyClass3X; }
void MyClass1::setFoo(int f) { impl(this, &MyClass1X::setFoo, f); }
void MyClass2::setFooBar(int f, int b) { impl(this, &MyClass2X::setFooBar, f, b); }
void MyClass3::setFooBarBaz(int f, int b, int z) { impl(this, &MyClass3X::setFooBarBaz, f, b, z); }
This is very basic sketch that should be further refined.
Pimpl idiom with Qt
Everything's is fine if i
#include QGraphicsScene
in this header but i would prefer to forward declare QGraphicsScene.
you cannot do it as you inherit from QGraphicsScene
:
class CustomScene : public QGraphicsScene
btw. this error has nothing to do with the way you implemented PIMPL idiom (actually it looks fine), inheritance from incomplete type is simply not possible as compiler needs to know the size / layout of your base class.
Pointer to implementation (PIMPL) in Qt
Using QScopedPointer
I managed to make it work like the way I wanted:
Dll include:
#define DllExport __declspec( dllexport )
#include <QScopedPointer>
namespace M{
class PPrivate;
class P
{
public:
DllExport P(/*some arguments*/);
DllExport ~P();
DllExport int aFunction (/* some arguments*/);
private:
Q_DECLARE_PRIVATE(P)
QScopedPointer<PPrivate> d_ptr;
};
}
Private include:
namespace M
{
class PPrivate
{
public:
# Other variables and functions which are not needed to be exported...
}
}
The CPP file:
DllExport M::P::P(/*some arguments*/)
:d_ptr(new PPrivate())
{
#...
}
DllExport M::P::~P(){ }
DllExport int M::P::aFunction (/* some arguments*/)
{
#...
}
Yet if somebody thinks there is a better idea, please share.
pimpl idiom struct memory leak
It does look like it should work..
What i find odd is the size of the leak, only 48 bytes.
I'd draw the conclusion that the MyClassImpl struct is freed, but something in it, isn't. Should the entire struct leak, the leak would be a lot bigger than 48 bytes.
But still, I can find no fault in that code.
Get Visual Leak Detector to enhance your debugging, it's free.
http://vld.codeplex.com/
Is the PIMPL idiom really used in practice?
So, I am wondering it this technique is really used in practice? Should I use it everywhere, or with caution?
Of course it is used. I use it in my project, in almost every class.
Reasons for using the PIMPL idiom:
Binary compatibility
When you're developing a library, you can add/modify fields to XImpl
without breaking the binary compatibility with your client (which would mean crashes!). Since the binary layout of X
class doesn't change when you add new fields to Ximpl
class, it is safe to add new functionality to the library in minor versions updates.
Of course, you can also add new public/private non-virtual methods to X
/XImpl
without breaking the binary compatibility, but that's on par with the standard header/implementation technique.
Data hiding
If you're developing a library, especially a proprietary one, it might be desirable not to disclose what other libraries / implementation techniques were used to implement the public interface of your library. Either because of Intellectual Property issues, or because you believe that users might be tempted to take dangerous assumptions about the implementation or just break the encapsulation by using terrible casting tricks. PIMPL solves/mitigates that.
Compilation time
Compilation time is decreased, since only the source (implementation) file of X
needs to be rebuilt when you add/remove fields and/or methods to the XImpl
class (which maps to adding private fields/methods in the standard technique). In practice, it's a common operation.
With the standard header/implementation technique (without PIMPL), when you add a new field to X
, every client that ever allocates X
(either on stack, or on heap) needs to be recompiled, because it must adjust the size of the allocation. Well, every client that doesn't ever allocate X also need to be recompiled, but it's just overhead (the resulting code on the client side will be the same).
What is more, with the standard header/implementation separation XClient1.cpp
needs to be recompiled even when a private method X::foo()
was added to X
and X.h
changed, even though XClient1.cpp
can't possibly call this method for encapsulation reasons! Like above, it's pure overhead and is related with how real-life C++ build systems work.
Of course, recompilation is not needed when you just modify the implementation of the methods (because you don't touch the header), but that's on par with the standard header/implementation technique.
Is this technique recommended to be used in embedded systems (where the performance is very important)?
That depends on how powerful your target is. However the only answer to this question is: measure and evaluate what you gain and lose. Also, take into consideration that if you're not publishing a library meant to be used in embedded systems by your clients, only the compilation time advantage applies!
PIMPL idiom for a pointer to a class in C++
Your implementation should use an interface (or in fact a class with only abstract methods) as a base class.
You cannot assign types in C++. You can only create typedefs and aliases, like that:
using PIMPLType = Compute_Prop;
However this won't work in your case.
This is how it should be implemented (also with possibility of multiple implementations):
class IImplementation
{
public:
Related Topics
Function With Missing Return Value, Behavior At Runtime
Dealing With Accuracy Problems in Floating-Point Numbers
Create an Array When the Size Is a Variable Not a Constant
Printing 1 to 1000 Without Loop or Conditionals
C++, What Does the Colon After a Constructor Mean
How to Find If a Given Key Exists in a C++ Std::Map
Appending a Vector to a Vector
How to Detect If I'M Compiling Code With a Particular Visual Studio Version
How to Properly Use Namespaces in C++
What Are the Differences Between .So and .Dylib on Macos
How Does Photoshop Blend Two Images Together
Differences Between C++ String == and Compare()
Are Exceptions in C++ Really Slow
The Program Can't Start Because Libgcc_S_Dw2-1.Dll Is Missing
How to Avoid Memory Leaks When Using a Vector of Pointers to Dynamically Allocated Objects in C++