Pimpl idiom
Declare the constructor and destructor in the header file and define them in the source file when using Pimpl idiom, even if they are empty.
Consider the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
If you do not declare the destructor the compiler will generate one in
every translation unit that includes impl.h
, which will call
the destructor of member variables, that is, the destructor
of m_impl
which requires the complete definition
of Impl
. But the whole purpose of Pimpl idiom is to hide the
definition of Impl
. To solve this problem you should declare
the destructor in the header file to prevent the compiler from generating
one, and define it in the source file. Then only the impl.cc
requires the
complete definition of Impl
. Other translation units just
call Pimpl
’s destructor as an external function, so they don’t
need to generate it.
Prefer anonymous namespace functions to class static functions.
When you are implementing a class’s interface, you may need some helper functions which has no relation with the private (member or static) data. You can either declare the helper functions as the private static functions in the class header file, or as the anonymous namespace functions in the class source file.
Since it does not need to access the private data, the helper function should be kept out of the class definition to make them loosely coupled. And it is the implementation’s details, which we don’t want the client of the class see it. So if we can put it outside the header file, we should do it. If we put it in the header file, every time the implementation changes the client using this class has to be re-compiled. So we should put the implementation details outside of the class header file as much as possible.
Pass read-only arguments by const reference, and read-write arguments by value
If the argument is intended to be read only in the function body, and
- if its size is bigger than the pointer type pass it by const-reference,
- otherwise, pass it by value
to avoid unnecessary copy. That means values of class objec type should be passed by const-reference, and values of the small types like the basic types should be passed by value. If the smaller ones were passed by const reference another pointer indirection cost would have been incurred in addition to the pointer copying cost when reading their values.
If the argument is intended to be modifed in the function body, it is recommended to pass it by value instead of passing it by reference and making a copy in the function body.
Consider the following code.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
If the argument passed is an lvalue a copy is required. But if the argument is an rvalue the copy can be optimized out by the compiler.
See “Want Speed? Pass by Value” for more details.
Don’t worry about returnning by value
Many modern C++ compilers provide the Return Value Optimization to elide the copy when returnning value.
Consider the following code.
1 2 3 4 5 6 7 8 9 10 |
|
Actually the signature of getName()
is translated by the compiler to
1 2 3 4 5 |
|
The caller allocates space for the return value on the stack, and pass the address of the space to the callee. Then the callee construct the return value directly in that space, which elimiates a copy from inside to outside. So we should no worry about the copy cost when returnning a big object from a function, and the signature is more satisfactory.