//
//  (C) Porkolab 2003
//
//  A.8.1.
//   
//  Compile-time dependences:

In C++  when anything in a header file changes, all code that includes
the header (either directly or indirectly) must be recompiled.

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


There are too many headers unneccessary included in x.h.
That is bad, because every client that includes x.h is
also forced to include them. In large projects that could
be painfull.


1. Remove <iostream>
    People automatically include <iostream>, even if input
    functions never used.

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

    class ostream;

    ..so we shall use <iosfwd>


But,

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

Here we use "os" as parameter to "print".
This also don't need definition, only declaration!


3. Replace "e.h" with forward declaration of class E;

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

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


C++ lets us encapsulate the private parts of a class
from unathorized access. It can take a little more work
to encapsulate dependencies on a class's privates.

Originally it was "impl_", the Hungarian notation of pointer
made it "pimpl_". Officially it is called "compilation firewall".


// file x.h
class X
{
    // public and protected members
private:
    // private members: 
    // whenever these change, 
    // all client code must be recompiled
};


We replace that with:


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


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


Advances:

1. Types mentioned only in a class's implementation need
    no longer be defined for client code, wich eliminate
    extra #includes and improve compile speed.

2. A class's implementation can be changed - private members
    can be added or removed - without replacing client code.

Costs:

1. Each construction/destruction must allocate/deallocate memory.

2. 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    
    class 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 Unnecsessary Inheritance


"Its not OO without inheritance" is a widely used idea.
"Is-a" relationship is a much stronger relationship
than "has-a" or "use-a". For the management of dependences
composition/membership is much better than inheritance.

"Use as strong relationship as neccessary but no stronger"


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

#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    
    class 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_;
};