Wednesday, November 6, 2013

Forward declarations vs includes in headers

What should be included and what can be forward declared in this header ?
class Polygon : public Shape {

public:
    void setBasicCurve (const Bezier& curve);
    void magnify (Vector direction);
    void setPoints (const vector<Point*>& points);

private:
    Plane*             m_surface;
    std::list<Point*>  m_points;
    Color              m_color;
    Bezier&            m_basic_curve;

    static Type        m_shape_type;

};

Here are some practical tips to keep your header files lean.

Member Variables

The compiler should know the size of a Polygon object to create it. Therefore, it must have enough information to determine the size of each member variable:
Plane*             m_surface;        // 1
    std::list<Point*>  m_points;         // 2
    Color              m_color;          // 3
    Bezier&            m_basic_curve;    // 4

    static Type        m_shape_type;     // 5

Polygon derives from Shape. Hence, it consists of its own members plus Shape's members. Therefore the Shape's size needs to be known.
#include "shape.h"
1. m_surface is a pointer, therefore its size is known. We should only forward declare it
struct Plane;
2. m_points is a std::list, and we can't know its size unless we know its structure and its contents. Therefore we include its declaration:
#include <list>
   m_points is a list of Point pointers, and all pointers have the same size. It's enough to declare that Point is a type:
struct Point;
3. m_color is a Color, and we can't determine its size unless we know its structure. Therefore we include its declaration:
#include "color.h"
4. m_basic_curve is a reference to a Bezier, and all references have the same size. It's enough to forward declare it:
struct Bezier;
5. m_shape_type is a static variable, thus it is not a part of the object, and we don't care about its size. Thus we can simply forward declare it:
struct Type;

Member Functions

Compiler needs to have enough information that these are function definitions:
void setBasicCurve (const Bezier& curve);         // 6
    void magnify (Vector direction);                  // 7
    void setPoints (const vector<Point*>& points);    // 8
6. if Bezier is a type, then this is a function definition. Forward declare it
struct Bezier;
7. if Vector is a type, then this is a function definition. Forward declare it.
struct Vector;
8. it is not practically possible to forward declare the std::vector (or any other STL containers). Always choose to include the STL headers:
include <vector>
Here is the resulting header:
#include <list>
#include <vector>
#include "shape.h"
#include "color.h"

struct Bezier;
struct Vector;
struct Plane;
struct Type;

class Polygon : public Shape {

public:
    void setBasicCurve (const Bezier& curve);
    void magnify (Vector direction);
    void setPoints (const vector<Point*>& points);

private:
    Plane*             m_surface;
    std::list<Point*>  m_points;
    Color              m_color;
    Bezier&            m_basic_curve;

    static Type        m_shape_type;

};


Summary

These are the basic rules for inclusion
  • include the headers of classes you inherit from
  • always include the headers of the STL containers
  • forward declare the types of static variables
  • forward declare the types of the variables held by reference or by pointer
  • include the header of all variables held by value
  • forward declare the function parameter types and the function return types

No comments:

Post a Comment