Header dependency is a nightmare.
#include <iostream>
#include <ostream>
#include <list>
#include "a.h"
#include "b.h"
#include "c.h"
#include "d.h"
#include "e.h"
class X : public A, private B
{
public:
X( const C&);
B f(int, char*);
C f(int, C);
C& g(B);
E h(E);
virtual std::ostream& print(std::ostream&) const;
private:
std::list<C> clist_;
D d_;
};
inline std::ostream& operator<<( std::ostream& os, const X& x)
{
return x.print(os);
}
-- Remove <iostream> People automatically include <iostream>, even if
input functions never used.
-- Replace <ostream> with <iosfwd> Parameters and return types only need
to be forward declared. Because ostream is basic_ostream<char> template,
it is not enough to declare.
-- Replace "e.h" with forward declaration of class E.
-- Leave "a.h" and "b.h": we need a full declaration of the base classes
in case of inheritance. The compiler must know the size of bases,
whether functions are virtual or not.
-- Leave "c.h" and "d.h": list<C> and D are private data members of X.
#include <iosfwd>
#include <list>
#include "a.h"
#include "b.h"
#include "c.h"
#include "d.h"
class E;
class X : public A, private B
{
public:
X( const C&);
B f(int, char*);
C f(int, C);
C& g(B);
E h(E);
virtual std::ostream& print(std::ostream&) const;
private:
std::list<C> clist_;
D d_;
};
inline std::ostream& operator<<( std::ostream& os, const X& x)
{
return x.print(os);
}
class X
{
private:
struct XImpl;
XImpl *pimpl_;
};
struct XImpl
{
};
Advantages:
-- Types mentioned only in a class's implementation need no longer be defined
for client code, wich eliminate extra #includes and improve compile speed.
-- A class's implementation can be changed - private members can be added
or removed - without replacing client code.
Costs:
-- Each construction/destruction must allocate/deallocate memory.
-- Each access of a hidden member can require at least one extra indirection.
Sometimes from the hidden part we must access members in visible part:
that we need another extra indirection.
#include <iosfwd>
#include "a.h"
#include "b.h"
class C;
class E;
class X : public A, private B
{
public:
X( const C&);
B f(int, char*);
C f(int, C);
C& g(B);
E h(E);
virtual std::ostream& print(std::ostream&) const;
private:
struct XImpl;
XImpl *pimpl_;
};
inline std::ostream& operator<<( std::ostream& os, const X& x)
{
return x.print(os);
}
#include "x.h"
#include "c.h"
#include "d.h"
struct XImpl
{
std::list<C> clist_;
D d_;
};
#include <iosfwd>
#include "a.h"
class C;
class E;
class X : public A
{
public:
X( const C&);
B f(int, char*);
C f(int, C);
C& g(B);
E h(E);
virtual std::ostream& print(std::ostream&) const;
private:
struct XImpl;
XImpl *pimpl_;
};
inline std::ostream& operator<<( std::ostream& os, const X& x)
{
return x.print(os);
}
#include "x.h"
#include "b.h"
#include "c.h"
#include "d.h"
struct XImpl
{
B b_;
std::list<C> clist_;
D d_;
};
PIMPL -- Compilation firewall -- Opaque type
Not only to reduce compilation time, but to provide binary compatibility
class X
{
public:
protected:
private:
struct XImpl;
XImpl *pimpl_;
};
Questions: -- What should go into XImpl Options are
-- Put all private data but not functions into XImpl.
Not too bad, but there is better
-- Put all private members into XImpl.
-- Put all private and protected members into XImpl.
Bad, protected members must be in X
-- Make XImpl entirely the class that X would have been, and write X as
only the public interface made up entirely of simple forwarding functions
handle body variant.
-- Does XImpl require a pointer back to the X object
Caveats
-- You can't hide virtual member functions in the Pimpl (here private
inheritance differs from membership)
-- Functions in Pimpl may require a "back pointer" to the visible object
(by convention that is called: "self_".)
-- Often the best compromise is to use Option 2, and in addition to put
into XImpl only rhose non-private functions that need to be called by
private ones.
-- 4th is better over 2nd not needed "back pointer", but X is useless
for inheritance.
FAST PIMPL
Since heap allocation is expensive, we are looking for solutions
to avoid heap usage.
class Y
{
static const size_t sizeofx = ;
char x_[sizeofx];
};
#include "x.h"
Y::Y()
{
static_assert( sizeofx >= sizeof(X) );
new(&x_[0]) X;
}
Y::~Y()
{
(reinterpret_cast<X*>(&x_[0]))->~X();
}
PIMPL and C++11/14
#ifndef PIMPL_H
#define PIMPL_H
#include <string>
#include <list>
class Pimpl
{
public:
Pimpl(const char *name_);
private:
std::string name;
std::list<std::string> childs;
};
#endif
#include "pimpl.h"
Pimpl::Pimpl(const char *name_) : name(name_) { }
#include "pimpl.h"
int main()
{
Pimpl p("Scott");
return 0;
}
The PIMPL version in C++98:
#ifndef PIMPL_H
#define PIMPL_H
class Pimpl
{
public:
Pimpl(const char *name_);
~Pimpl();
private:
struct Impl;
Impl *pImpl;
};
#endif
#include "pimpl.h"
#include <string>
#include <list>
struct Pimpl::Impl
{
public:
Impl(const char *name_);
private:
std::string name;
std::list<std::string> childs;
};
Pimpl::Pimpl( const char *name_) : pImpl(new Impl(name_)) { }
Pimpl::~Pimpl() { delete pImpl; }
Pimpl::Impl::Impl(const char *name_) : name(name_) { }
#include "pimpl.h"
int main()
{
Pimpl p("Scott");
return 0;
}
Why not use unique_ptr in C++14?
#ifndef PIMPL_H
#define PIMPL_H
#include <memory>
class Pimpl
{
public:
Pimpl(const char *name_);
private:
struct Impl;
std::unique_ptr<Impl> pImpl;
};
#endif
#include "pimpl.h"
#include <string>
#include <list>
struct Pimpl::Impl
{
public:
Impl(const char *name_);
private:
std::string name;
std::list<std::string> childs;
};
Pimpl::Pimpl( const char *name_) : pImpl(std::make_unique<Impl>(name_)) { }
Pimpl::Impl::Impl(const char *name_) : name(name_) { }
$ g++ -std=c++14 -c -Wall pmain.cpp pimpl.cpp
In file included from /opt/compass/cc/trunk/trunk-deps/include/c++/4.9.2/memory:81:0,
from pimpl.h:7,
from pmain.cpp:4:
/opt/compass/cc/trunk/trunk-deps/include/c++/4.9.2/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Pimpl::Impl]’:
/opt/compass/cc/trunk/trunk-deps/include/c++/4.9.2/bits/unique_ptr.h:236:16: required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Pimpl::Impl; _Dp = std::default_delete<Pimpl::Impl>]’
pimpl.h:9:7: required from here
/opt/compass/cc/trunk/trunk-deps/include/c++/4.9.2/bits/unique_ptr.h:74:22: error: invalid application of ‘sizeof’ to incomplete type ‘Pimpl::Impl’
static_assert(sizeof(_Tp)>0, ^
$
???
The problem comes from the fact that Pimpl::Impl is an incomplete type,
and the default deleter of unique_ptr tries to create a code to delete it.
#ifndef PIMPL_H
#define PIMPL_H
#include <memory>
class Pimpl
{
public:
Pimpl(const char *name_);
~Pimpl();
private:
struct Impl;
std::unique_ptr<Impl> pImpl;
};
#endif
#include "pimpl.h"
#include <string>
#include <list>
struct Pimpl::Impl
{
public:
Impl(const char *name_);
private:
std::string name;
std::list<std::string> childs;
};
Pimpl::Pimpl( const char *name_) : pImpl(std::make_unique<Impl>(name_)) { }
Pimpl::~Pimpl() {}
Pimpl::Impl::Impl(const char *name_) : name(name_) { }
With shared pointer there is no similar issue. This is because in unique_ptr
the deleter is part of the type (and therefore the compiler generates smaller,
faster code), while in the case of shared_ptr the deleter type is not part of
the smart poiter's type. This is larger and slower, but (1) one can provide
deleters in run-time, and (2) shared_ptr pointed types can be incomplete when
special member functions are generated.
#ifndef PIMPL_H
#define PIMPL_H
#include <memory>
class Pimpl
{
public:
Pimpl(const char *name_);
private:
struct Impl;
std::shared_ptr<Impl> pImpl;
};
#endif
#include "pimpl.h"
#include <string>
#include <list>
struct Pimpl::Impl
{
public:
Impl(const char *name_);
private:
std::string name;
std::list<std::string> childs;
};
Pimpl::Pimpl( const char *name_) : pImpl(std::make_shared<Impl>(name_)) { }
Pimpl::Impl::Impl(const char *name_) : name(name_) { }