Slow build times are a common problem in C++. The build speed is based on language complexity and code organization. While you may not be able to change C++ language complexity, you can improve code organization. As Herb Sutter said, “Managing dependencies well is an essential part of writing solid code.”

The more modular and less interdependent (complex) your code is in general, the less often you will have to recompile everything. This will reduce the amount of work the compiler has to do on any individual block at the same time, because the compiler has less that it needs to keep track of in memory.

Today we will talk about header dependencies and their effect on build times.

Direct includes not needed

One of the main problems affecting the speed of C++ build times is the unnecessary inclusion of header files. Header inclusion should be done only when needed. For example, if you are using only classes X and Y, then you only need to include x.h and y.h. Unfortunately, many programmers habitually include many more headers than necessary, like <iostream> or windows.h. This can seriously degrade build time.

The Chromium Projects C++ Dos and Don’ts recommends not including unneeded headers. They mention that, after refactoring a file, there may often be symbols that are no longer used in that header, meaning you can remove that header. With that in mind, when you are refactoring it is a good idea to track redundant includes either manually or using an external tool like Lattix Architect.

Indirectly included files

Another way to reduce header dependencies, and therefore build times, in C++ is to avoid including headers inside other header files. In C++, you get the declaration of a function by including its header file, which can be put in either a .cpp file or a header file. When you include a header in another header file, you may be slowing down compilation time because you may be including other files unnecessarily.

The solution is a forward declaration. A forward declaration of a function or class simply introduces a name. According to Wikipedia: A forward declaration is a declaration of an identifier for which the programmer has not yet given a complete definition. This can be used in situations where you need to know that the name of a class is a type, but not necessarily the structure. In C++, classes can be forward-declared if you only need to use the pointer-to-that class type or reference, since all pointers and references are the same size and can have the same operations performed on them.

This is useful inside a class definition if a class contains a member that is a pointer (or reference) to another class. If, on the other hand, you need to create an object in the header file you can’t use forward declaration because a forward declaration does not tell you how big it is or anything about member functions or constructors/destructors. Forward declarations significantly reduce build time by avoiding unnecessary coupling. Forward declarations reduce build times in two ways:

  1. By reducing the amount of files that the compiler has to open and process
  2. By saving on unnecessary recompilation. If you include the header, you will be forced to recompile the code even if the change is unrelated.

It turns out in my last refactoring blog tip, I had an indirectly included file (header file included in another header file:

Lattix Architect find issues

As you can see I included shareprice.h in stock30.h:

Lattix Architect dependency usage

The next section will show you how I fixed this issue.

How to fix indirectly included files

Here is my original stock30.h file:

#include <string>
#include "shareprice.h"

class Stock
{
private:
    std::string company;
    int shares;
    SharePrice share_val;
public:
    Stock();                  // default constructor
    Stock(const std::string & co, long n = 0; double pr = 0.0);
    void buy(long num, double price);
    void sell(long num, double price);
    void update(double price);
};

In stock30.h, I included “shareprice.h”. One way to fix this issue is by making the SharePrice class a forward declaration. I do this by:

  1. Removing #include “shareprice.h”
  2. Adding class SharePrice
  3. Changing SharePrice share_val to a pointer to SharePrice

You can see the updated code below:

#include <string>

class SharePrice;          // Forward declaration

class Stock
{
private:
    std::string company;
    int shares;
    SharePrice* share_val;       // changed to a pointer to SharePrice
public:
    Stock();                   // default constructor
    Stock(const std::string & co, long n = 0, double pr = 0.0);
    void buy(long num, double price);
    void sell(long num, double price);
    void update(double price);
};

You will also need to update the stock30.cpp file to reflect the change in the variable share_val, but I will leave that as an exercise for the reader.

Summary

Build times are a constant problem for larger C++ programs. But by thinking carefully about a C++ project’s design (especially for large projects, consisting of multiple modules), you can modify it so the compiler can produce output efficiently. This can be done manually or with an architectural analysis tool like Lattix Architect.