Search: 

Machine translation:
MBBSoftware
Creating software for businesses, professionals, and the home
our sites: MBBSoftware - Custom Image Presenter - Authenticate Testimonial
MBBSoftware Blog - Including Header File vs Forward Declaration in C++ Programming.aspx Hi guest
Sign up
Login

Including Header File vs Forward Declaration in C++ Programming


By Miroslav B. Bonchev
The C++ programming language paradigm provides two mechanisms which can be used to inform one entity about the existence of another. These are (a) inclusion of the header file containing the declaration of the former entity, and (b) forward declaration. There is a dispute among programmers as to which of these two is the better approach. In this article I take the position that inclusion of the header file is the correct approach and explain why. Nevertheless there are cases when forward declaration is admissible and in fact the only possibility.

Perhaps you might have heard the proverbial story about the developer who started developing a dog house which was very successful, so he kept adding to it. Unfortunately it couldn’t quite make it to becoming a skyscraper because it collapsed under its own weight. This anecdote suggests that the essence of the software is its architecture. For the purpose of this article I will assume that this is a self-evident truth. However, for a larger discussion see my other articles on the subject. Provided that this premise is true, it follows that it does not really matter if the software is compiled with Visual Studio or another compiler or if the compilation took 1 minute or 1 hour, or whether the compiled code is slightly smaller or larger, faster or slower, etc. If the architecture is wrong then nothing else really matters. That is not necessarily true if the project is sufficiently small, i.e. if the developer we mentioned earlier stopped any further development as soon as he finished the dog house, or if his dog doesn’t really mind the cold wind in the winter. However if the architecture is sufficiently poor then even a doghouse will collapse under its own weight. I will assume this foundational premise, which is that any code must be as architecturally sound as possible, and will continue with the problem we are concerned with in this paper.

In the C/C++ languages, types are described in pairs of files. The first file in each pair is the header file or .h file and is intended to be the placeholder of the declaration of the type. The second file is the implementation or .cpp file and is used to define the actual operation of the type. We formalize the following definition.

Definition 1: A type is completely defined using a pair of files:
  1. The declaration (.h) file contains the blueprint of one type (class).
  2. The implementation (.cpp) file contains the function of the type declared in the .h file.
For consistency and unity the names of the files match the name of the defined in/by them type.

From this definition follows the obvious corollary:

Corollary 2: There is a one to one correspondence between a type and its .h/.cpp pair of files.
Proof:
  1. The type is completely defined in the pair of files.
  2. The pair of files contains no other declarations except of the type they define.
    QED


Note 1: Due to relaxed rules in the compiler implementations, the border between .h and .cpp files is often fuzzy since definitions are often placed in the header files and vice versa. Further, due to undue limitations of the C++ language, such as the lack of local functions, declaring of local classes is common and often used to overtake the lack of local functions. Further, for small types the overhead to provide pairs of files is too large and developers prefer to contain such small entities in a single place as well as many of them.

Note 2: Due to issues with templates they are typically defined only in .h files.

Note 3: In the C programming language paradigm, there are no classes, but only structures. Functions are however often separated in .h/.c files where the .h file contains the declaration (prototype) of the function and the .c file contains its definition, i.e. body.


Definition 3: Forward declaration is a two word specification in the form class [Name]; or struct [Name]; - where [Name] is the name of the forward declared type.


From this definition it clear that forward declaration only informs the compiler for the possible/assumed existence of a type with that particular name. The proponents of forward declaration typically cite the following advantages:
  1. Faster compilation.
  2. Total encapsulation in terms of hiding of everything about the declared class.


However, from our foundational premise which we established in the beginning it follows that:
  1. Faster compilation is entirely irrelevant to the quality of code and thus is dismissed.
  2. "Total encapsulation" – while encapsulation of data is one of the most important concepts when discussing architectural design, the above technique is not at all encapsulation but is a "because" and in essence declaring a void pointer to be later interpreted.
    - On a humorous note, this kind of "total encapsulation" reminds one about the misfortunate Earnest T. Brass who instead of stealing the beautiful Charlene stole the "totally" encapsulated (totally wrapped up in a marriage gown) Barney Fife.


Although to the careful reader these reasons are sufficient to dismiss Forward Declaration as a very poor practice to be always avoided unless necessary, we will continue with more examples and reasoning.


The Forward Declaration proponents insist on always using it except in the following cases. Forward declaration should not be used when one needs to:
  • Access a property or method of the class or any of its ancestors.
  • Use pointer arithmetic (Note of the author: this is not always true).
  • Use of the sizeof operator, or mem-copy of object.
  • Use of Real Time Type information.
  • Construction or destruction.
  • Any reference to an object of this type.
  • Derive a class from the forward declared type.


It must be noted that these are not mere suggestions, as its proponents seem to suggest. In fact these are the cases when the use of Forward Declaration leads to compilation errors and simply cannot be used. In summary, forward declaration cannot be used whenever an object of this type is used in whatsoever meaningful way, other than "An unidentified type called X may exist." This leads to the following informal proposition:

Proposition 4: A Forward Declared type is a "UFO" type. ("UFO" stands for the usual "Unidentified (Flying) Object")
Proof:
  1. The meta object (forward declared type) may or may not exist.
  2. If the meta object (forward declared type) exists, it has an entirely unknown nature.
  3. Based on the used keyword class or struct, one could invoke very limited, and generalized speculations on its nature, provided that the type exists in the first place.
    QED

Notation 5: Forward Declared type can be interchangeably called a UFO type.
Motivation: Follows immediately from Proposition 4.


Note 1: Thus it is fair to say that using forward declaration turns computer science into speculative broth.
Note 2: For completeness, we will mention that the alternative to C/C++ approach used in languages such as C# is to have only one file in which types are defined entirely. In this case there is no separation between declaration and definition – in fact there is no declaration at all, so that the entire content of the defined type is placed in one single file. The compiler/interpreter parses the files first to collect all declarative information it needs before compilation.


We continue with various arguments against using UFO types (forward declared types).

Argument A: By definition, the .h file is the interface file of the type, using .h files:
  1. Helps to maintain meaningful the relations and dependencies between types and objects, i.e. stimulates well-constructed code.
  2. Further stimulates better quality of code:
    1. Disallows circular declarations – circular definitions in a broader sense are a common mistake when definitions are not made carefully. The use if .h files in a circular manner leads to infinite regression during compilation, and is immediately noticed. Conversely, forward declaration does not and intrinsically cannot detect them. Thus the use of .h files is the preferred approach.
    2. Placing code in .h files increases the compilation time – thus using .h files stimulates correct placement of code in .cpp files (instead of .h files), thus maintaining the .h file as the interface file and .cpp file as the definition file. It also stimulates minimal inclusion of headers, preventing redundant declarations and inclusions and thus stimulates better understanding of the system, as well as creating well-structured hierarchic systems.
  3. Exposes the interface of the class – note that this was one of the supposed advantages of the forward declaration. The definition of interface in Computer Science is a "Point of interaction between two components." Hence, the .h file inclusion publishes those methods and properties which are made available to access by the type, which is precisely the objective of an interface. In difference, the private methods and properties are known only to the "client" developer, but they exists in at least 2 meta-levels above any instantiated objects, so that they can consider the architectural consistency of the model they build, whilst the private properties and methods are entirely hidden to any types and (potential) objects which might try to use them. The same optimal exposure/concealment applies to protected methods and properties.

    Using forward declaration to hide properties has the following implications:
    • Hides everything including the public methods and properties of the type, but this is a violation of the concept of access rights in type declarations.
    • It prohibits the use of public nested in the forward declared type declarations.
    • Since Forward Declaration hides the properties of the forward declared type entirely, it stimulates using "public" access control declaration everywhere in those types. The latter significantly deteriorates the quality of code.
  4. As a consequence of the above, .h file inclusion keeps the attention of developers in high alert and requires them to produce better than otherwise quality of code.
  5. Inclusion of .h files leads to easier to understand code and system.




Argument B: An argument has been made in favor of Forward Declaration, in particular for cases when one organization provides code to another. In this respect it is needed to make a clear distinction between software designed for internal consumption and software designed for use by external organizations, where the software provides service and is not provided as "Know How".

Clearly, this argument is ill aimed, but even if so, when providing services using interfaces in COM sense, interfaces in sense of .h files containing library function prototypes, and handles are the optimal options, with no viable alternative. Clearly, to use either, one needs to include the .h files provided by the vendor. Forward declared types in this sense is no better than ludicrous.


Argument C: Some have argued that using forward declaration should be used when the respective type is used only for parameters in methods/function prototypes.

A method part of a type implements code for doing a particular piece of work, and is using the set parameters passed to it. Any method should be well defined, including its purpose, parameters and results at the point of its declaration (.h file). This requires that the architect of the method declaration must know the interface and nature of each passed parameter (object) in order to ensure that it is fit for purpose.

For example: what data and services it can receive from it, as well as what parameters it requires and what exceptions it possibly throws.

However, if forward declared, the parameter type is not known at all. Not even if it is instantiable, much less its properties. This leads us to conclude that the author of the method cannot guarantee the integrity of its definition, and hence the integrity of the type they are constructing. Therefore the use of Forward Declaration in this case is contrary to the designing of well-defined and consistent code.

The use of Forward Declaration implies that the software systems development must be code driven, i.e. write the code in the .cpp file, and then declare the finished method in the .h file, as opposed to architecture driven. However, this was exactly the proverbial dog-house building software approach.


Argument D: Argument D: By definition, header files are declaration containers representing the declaration of the type. A declaration must be well defined, that is consistent i.e. Complete and Non self-contradictory. However, a forward declaration is entirely incomplete, and can by no means guarantee to be non-self-contradictory. Hence, the use of forward declaration is inconsistent, and therefore must be avoided.

For example, it is clear that a forward declaration is entirely incomplete. Suppose someone else implements a type which we forward declare. We use stubs in order to progress our work, expecting that they will deliver on their promise. However, they may find that they cannot deliver the promised type, because it is impossible to implement. Unless we are able to remove the forward declared type entirely, our work becomes useless, which may still be the case even if we are able to remove the forward declared type.


Argument E: Forward declaration can be only used if all pointers have the same size. Although this is usually the case, it is neither mandatory nor necessary. If pointers to objects have different size then forward declaration cannot be used unless one instructs the compiler for the size of the pointer to that object. However, if the size of the pointer to an object of forward declared type is dynamic then forward declaration becomes entirely impossible except for purely declarative statements, such as reserving the name of a type and friend declaration. Even though, typically pointers have the same size, this argument shows that forward declaration is fundamentally inconsistent, except for purely declarative statements, such as reserving the name of a type and friend declaration


Argument F: Suppose that we define an abstract class which is a "pure" interface type containing only public abstract virtual methods. Forward declaration on such a type (interface) is equally legitimate as forward declaration on any other type, but it is clear that in this case it entirely defeats the purpose of the type. In fact forward declaration in this case can be classified as nonsense.


Argument G: Forward declaration has semantics of Pointer to unknown type with known name. Thus it differs from a void* only by the known name. Thus if we extend the void* semantics to a [void*, const unsigned long] pair, the second element of which is used as differentiator to make appropriate casting of the void*, e.g. in a switch statement. The latter approach defies not only the Object Oriented Paradigm, but even very basic good programming techniques, and is clearly unwanted. However, the difference between it and the forward declaration is only cosmetic.


Conclusion


Forward declaration is a tool necessary in cases such as the following:
  • Declare friend type.
  • Reserve a name for a type.
  • Allow implementation of mutually dependent entities. For example definition of mutually dependent interfaces, in which there are methods returning pointer to the other interface.
  • Allow implementation of dependent types placed in the same file.

However, forward declaration has been abused, especially by the Unix community, using it instead of interface .h files. Perhaps the reason for this is that some developers are not willing to do what is necessary to maintain a well-structured hierarchy of types together with their .h/.cpp files. Others may be not fully appreciating the principles of Consistency and Object Oriented Programming. Yet third, might be simply lazy and/or sloppy.

The proponents of forward declaration site faster compilation as main advantage. However, slow compilation due to .h file inclusion, is (a) irrelevant to the quality of the system, b – suggests that there is poor programming including definitions in the h files (wrong places), multiple inclusions, unnecessary inclusions, while .h file inclusions must reflect the relationships, including inheritance, between types.
Miroslav B. Bonchev
26-th December 2013
England
We would love to know your thoughts and opinions on this article. Please leave any comments or questions you may have about it in the box below, and create a free account or subscribe to our newsletter if you wish to be notified when we publish new articles.
Community Content
(To enter your comments you must be signed in. Log in or create FREE account.)
MemberComments
Be the first to comment.
Products
Act On File
Audio Control
Custom Image Presenter
Photo Window
Vat # Validator
Custom Image Presenter
Homepage
for Galleries and Museums
for Hotels, Resorts and Cruises
for Parks of any kind
for Any Business
Learning
Encryption and Authentication
Safe Online Communication
Authenticable Website Testimonials
Learn how to store private keys
Make The Most From Your Files
Convenient Volume Control
Photo Window - an Awesome Gift
Support
My Account
FAQ - Forum
 
Community
Blog
Email this page
Newsletter
MBBSoftware
About
Contact
Buy Now
Download
Public Authentication Key
Public Encryption Key

Sitemap
Disclaimer
Privacy
Antispam
© Copyright 2017 MBBSoftware. All Rights Reserved.

Email this page
To:
use semicolon to separate emails eg: joe@abc.com; lea@abc.com
Subject:
Message:
a link to this page will be automatically added to your message
From:
Please type the anti-bot text below.
Type text:
Thank you for subscribing to the MBBSoftware newsletter.
Enter your email address:
Please type the anti-bot text below.
Type text: