Header dependency is a nightmare.


//
// Include files
//

// x.h: sample header
// version 1

#include <iostream>
#include <ostream>
#include <list>

// none of A, B, C, D, E are templates
// Only A and C have virtual functions

#include "a.h"      // class A
#include "b.h"      // class B
#include "c.h"      // class C
#include "d.h"      // class D
#include "e.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);
}


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


// x.h: sample header
// version 2

#include <iosfwd>
#include <list>

// none of A, B, C, D, E are templates
// Only A and C have virtual functions

#include "a.h"      // class A
#include "b.h"      // class B
#include "c.h"      // class C
#include "d.h"      // class D

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);
}



//
// Pimpl
//

// file x.h
class X
{
    // public and protected members
private:
    // pointer to forward declared class
    struct XImpl;
    XImpl *pimpl_;  // opaque pointer
};

// file x.cpp
struct XImpl   // not neccessary to declare as "class"
{
    // private members; fully hidden
    // can be changed at without
    // recompiling clients
};



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.


// x.h: sample header
// version 3

#include <iosfwd>
#include "a.h"      // class A
#include "b.h"      // class B

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:
    // opaque pointer to forward-declared class   
    struct XImpl;
    XImpl *pimpl_;
};

inline std::ostream& operator<<( std::ostream& os, const X& x)
{
    return x.print(os);
}


// file x.cpp

#include "x.h"
#include "c.h"      // class C
#include "d.h"      // class D

struct XImpl
{
    std::list<C>    clist_;
    D               d_;
};



//
// Removing inheritance
//

// x.h: sample header
// version 4

#include <iosfwd>
#include "a.h"      // class A

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:
    // opaque pointer to forward-declared class   
    struct XImpl;
    XImpl *pimpl_;
};

inline std::ostream& operator<<( std::ostream& os, const X& x)
{
    return x.print(os);
}

// file x.cpp

#include "x.h"
#include "b.h"      // class B
#include "c.h"      // class C
#include "d.h"      // class D

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



//
// Pimpl
//

// file x.h
class X
{
public:
    // public members
protected:
    // protected members
private:
    // pointer to forward declared class
    struct XImpl;
    XImpl *pimpl_;  // opaque pointer
};



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.



// file: y.h

class Y
{
    //...
    static const size_t sizeofx = /* ... */;
    char x_[sizeofx];
};


// file: y.cpp

#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



//
// pimpl.h
//
#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 /* PIMPL_H */


//
// pimpl.cpp
//
#include "pimpl.h"

Pimpl::Pimpl(const char *name_) : name(name_) { }

//
// pmain.cpp
//
#include "pimpl.h"

int main()
{
  Pimpl p("Scott");
  return 0;
}



The PIMPL version in C++98:


//
// pimpl.h
//
#ifndef PIMPL_H
#define PIMPL_H

class Pimpl
{
public:
  Pimpl(const char *name_);
  ~Pimpl();
private:
  struct Impl;
  Impl *pImpl;
};

#endif /* PIMPL_H */


//
// pimpl.cpp
//
#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_) { }


//
// pmain.cpp
//
#include "pimpl.h"

int main()
{
  Pimpl p("Scott");
  return 0;
}


Why not use unique_ptr in C++14?


//
// pimpl.h
//
#ifndef PIMPL_H
#define PIMPL_H

#include <memory>

class Pimpl
{
public:
  Pimpl(const char *name_);
private:
  struct Impl;
  std::unique_ptr<Impl> pImpl;
};

#endif /* PIMPL_H */


//
// pimpl.cpp
//
#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.




//
// pimpl.h
//
#ifndef PIMPL_H
#define PIMPL_H

#include <memory>

class Pimpl
{
public:
  Pimpl(const char *name_);
  ~Pimpl();	// reintroduce destructor to avoid generating one
private:
  struct Impl;
  std::unique_ptr<Impl> pImpl;
};

#endif /* PIMPL_H */


//
// pimpl.cpp
//
#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() {}    // define destructor here.
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.


//
// pimpl.h
//
#ifndef PIMPL_H
#define PIMPL_H

#include <memory>

class Pimpl
{
public:
  Pimpl(const char *name_);
  // ~Pimpl();
private:
  struct Impl;
  std::shared_ptr<Impl> pImpl;
};

#endif /* PIMPL_H */


//
// pimpl.cpp
//
#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::~Pimpl() {}
Pimpl::Impl::Impl(const char *name_) : name(name_) { }