C++ Programming Guide

Header Files

Creating an effective header file can be difficult. Often your header file must adapt to different versions of both C and C++. To accommodate templates, make sure your header file is tolerant of multiple inclusions (idempotent), and is self-contained.

Language-Adaptable Header Files

You might need to develop header files for inclusion in both C and C++ programs. However, Kernighan and Ritchie C (K&R C), also known as "classic C," ANSI C, Annotated Reference Manual C++ (ARM C++), and ISO C++ sometimes require different declarations or definitions for the same program element within a single header file. (See the C++ Migration Guide for additional information on the variations between languages and versions.) To make header files acceptable to all these standards, you might need to use conditional compilation based on the existence or value of the preprocessor macros _ _STDC_ _ and _ _cplusplus.

The macro _ _STDC_ _ is not defined in K&R C, but is defined in both ANSI C and C++. Use this macro to separate K&R C code from ANSI C or C++ code. This macro is most useful for separating prototyped from nonprototyped function definitions.


#ifdef 
_ _STDC_ _
int function(char*,...);      // C++ & ANSI C declaration
#else
int function();               // K&R C
#endif 

The macro _ _cplusplus is not defined in C, but is defined in C++.


Note -

Early versions of C++ defined the macro c_plusplus instead of _ _cplusplus. The macro c_plusplus is no longer defined.


Use the definition of the _ _cplusplus macro to separate C and C++. This macro is most useful in guarding the specification of an extern "C" interface for function declarations, as shown in the following example. To prevent inconsistent specification of extern "C", never place an #include directive within the scope of an extern "C" linkage specification.


#include "header.h"
...                     // ... other include files ... 
#if defined(_ _cplusplus)
extern "C" {
#endif
  int g1();
  int g2();
  int g3()
#if defined(_ _cplusplus)
}
#endif

In ARM C++, the _ _cplusplus macro has a value of 1. In ISO C++, the macro has the value 199711L (the year and month of the standard expressed as a long constant). Use the value of this macro to separate ARM C++ from ISO C++. The macro value is most useful for guarding changes in template syntax.


// template function specialization
#if _ _cplusplus < 199711L
int power(int,int);                       // ARM C++
#else
template <> int power(int,int);           // ISO C++
#endif

Idempotent Header Files

Your header files should be idempotent. That is, the effect of including a header file many times should be exactly the same as including the header file only once. This property is especially important for templates. You can best accomplish idempotency by setting preprocessor conditions that prevent the body of your header file from appearing more than once.


#ifndef HEADER_H
#define HEADER_H
/* contents of header file */
#endif

Self-Contained Header Files

Your header files should include all the definitions that they need to be fully compilable. Make your header file self-contained by including within it all header files that contain needed definitions.


#include "another.h"
/* definitions that depend on another.h */

In general, your header files should be both idempotent and self-contained.


#ifndef HEADER_H
#define HEADER_H
#include "another.h"
/* definitions that depend on another.h */
#endif

Unnecessary Header File Inclusion

Programs written in C++ typically include many more declarations than do C programs, resulting in longer compilation times. You can reduce the number of declarations through judicious use of several techniques.

One technique is to conditionally include the header file itself, using the macro defined to make it idempotent. This approach introduces an additional interfile dependence.


#ifndef HEADER_H
#include "header.h"
#endif


Note -

System header files often include guards of the form _Xxxx, where X is an uppercase letter. These identifiers are reserved and should not be used as a model for constructing macro guard identifiers.


Another way to reduce compilation time is to use incomplete class and structure declarations rather than including a header file that contains the definitions. This technique is applicable only if the complete definition is not needed, and if the identifier is actually a class or structure, and not a typedef or template. (The standard library has many typedefs that are actually templates and not classes.) For example, rather than writing:


#include "class.h"
a_class* a_ptr;

write:


class a_class;
a_class* a_ptr;

(If a_class is really a typedef, the technique does not work.)

One other technique is to use interface classes and factories, as described in the book Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, Addison Wesley, 1994.