5

I have an example with the custom Stack implementation. As far as I understood, the .h files should only contain declarations and the cpp files should only contain implementations. I have found an example of a custom Stack on the cplusplus.com/stack_example and it looks something like the following.

Stack.h file

#ifndef _STACK_H_
#define _STACK_H_

#include <iostream>
#include "Exception.h"

template <class T>
class Stack {
    public:
        Stack():top(0) {
            std::cout << "In Stack constructor" << std::endl;
        }
        ~Stack() {
            std::cout << "In Stack destructor" << std::endl;
            while ( !isEmpty() ) {
                pop();
            }
            isEmpty();
        }

        void push (const T& object);
        T pop();
        const T& topElement();
        bool isEmpty();

    private:
        struct StackNode {              // linked list node
            T data;                     // data at this node
            StackNode *next;            // next node in list

            // StackNode constructor initializes both fields
            StackNode(const T& newData, StackNode *nextNode)
                : data(newData), next(nextNode) {}
        };

        // My Stack should not allow copy of entire stack
        Stack(const Stack& lhs) {}

        // My Stack should not allow assignment of one stack to another
        Stack& operator=(const Stack& rhs) {}
        StackNode *top;                 // top of stack

};

Now I have question. This .h file obviously reveals some implementation details. The constructor and the destructor are both implemented in the .h file. In my understanding, those should be implemented in .cpp file. Also, there is that struct StackNode which is also implemented in the .h file. Is that even possible to implement in the .cpp file and only declare it in the header file? As a general rule, wouldn't it be better if those were in the .cpp implementation file? What would be the best way to code this thing so that it follows C++ rules?

Whizzil
  • 1,264
  • 6
  • 22
  • 39
  • 6
    You put things in a header file that needs to be used by multiple [translation units](http://en.wikipedia.org/wiki/Translation_unit). If you want to hide implementation details, consider using [the pimpl idiom](http://c2.com/cgi/wiki?PimplIdiom), or other opaque structure pointers. – Some programmer dude Oct 20 '15 at 10:41

3 Answers3

6

Header files are not fundamentally different from source files.

A header can, in principle, contain the same code constructs that a source file can. The only thing that, by convention, distinguishes a header from a source file is that a header is supposed to be #included by other files, which we usually do not do with source files (although we could, if we felt adventurous).

The important thing now is what happens if a file gets #included by more than one source file. Mind you, this is basically equivalent to copy-pasting the same code to multiple .cpp files. There are certain things that will likely get you into trouble in this case.

In particular, if you end up with two definitions for the same symbol in two different source files, your program is not well-formed (according to the one-definition rule) and the linker will probably refuse to link your program.

With distinct source files this usually not a problem, unless you accidentally reuse a name in different contexts. As soon as header files step into the picture (which are just copy-pasted code), things start to look different. You can still put a definition in a header file, but if that header file then gets pulled in by more than one source file, you have yourself a nice violation of the one-definition-rule.

Therefore, the convention is to only put stuff into header files that is safe to be duplicated across multiple source files. This includes declarations, inline-function definitions and templates.

The key realization here is probably that header files are a rather archaic tool for sharing code between source files. As such, they rely heavily on the user being smart enough not to mess things up.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
5

There is no standard rule on what must be in a header file, exception made for Standard Library headers. I mean, theoretically speaking we could completely avoid header files and copying and pasting declarations around in .cpp files.

That said, it's more a matter of common sense and experience. You will put stuff into headers or .cpps according to your will and to your needs. We could list some use cases:

  • template (as in your example) declarations and implementations usually go in headers. See this thread for more info.

  • you do put function declarations in a header when you think/want they will be used in more than a translation unit

  • you don't put function declarations in a header when you don't want/think them to be used in other translation units

  • if you want a function to be inlined and want that function to be used in different translation units, then you put its definition in a header (preceded by the inline keyword if it's a non-member function)

  • you do put a class declaration (aka forward declaration) in a header when you think/want that objects of that type will be accessed* from different translation units

  • you don't put a class declaration inside a header when you think/want that class to be accessed only in one translation unit

  • you do put a class definition (i.e. the whole class interface) in a header when you think/want that objects of that type will be created* in different translation units

  • you don't put a class definition in a header when you want that objects of that type will be created only in one translation unit

  • if you want to define a global variable, you hardly put the definition in a header file (unless you want to include that header just in a single translation unit)

  • if you are developing a library, you will put declarations of those functions and classes you want to provide the user into header files

  • if you are developing a library, you will find a way to put into .cpp files those implementation details you don't want to make public (as @Joachim Pileborg suggested, see the pimpl idiom)

  • you usually don't want to put using declaration or using directives in a header, because they will pollute those translation units that will #include the header

  • when it's possible, you don't want to #include other headers into yours; you definitely do prefer to forward declare what you need to make your program compile

  • finally, roughly speaking, the less stuff you put inside a header, the faster your files will compile; and, let me state the obvious, you do want your files to compile fast!


Notes

Above I've talked mainly of classes and functions, but in general those rule of thumbs are valid for enumerations and typedef declarations.

Member function definition in a header is missing from the list, because it's a matter of function definition inside or outside class definition, not of .h vs .cpp files

* With accessed I mean used through a pointer or a reference, in contrast to created

Community
  • 1
  • 1
Paolo M
  • 12,403
  • 6
  • 52
  • 73
  • That's a promising list, but (currently) omits mention of one of the most interesting cases - class/struct definitions. – Tony Delroy Oct 20 '15 at 13:34
  • @TonyD It was worst than missing, I made confusion between class *definition* and *declaration*, so the answer was misleading. I tried to clarify that point. Please, fell free to improve the current list by editing or adding further points. – Paolo M Oct 20 '15 at 14:41
4

What exactly goes into a header file?

Indipendently to what you learn and from your design, put as less stuff as possible into headers. Headers are the minimal stuff required to use implementation details. Simpler headers usually also returns in much simpler to use code and their inclusion has smaller compile times compared to big headers.

Of course in C++ headers are not different from source files, it is upon to your skill to craft source code into different files to make your project simpler to use and to understand.

There are cases in wich you are forced to put stuff in headers (using templates in example), if you are not forced to do so it is better to put stuff into header & source files.

The compiler will convert to a binary ANY source file (header or cpp), the only requisite is that there's at least some "compilable" code (the body of a function / method, even if empty).

The one who makes distinction is the programmer:

If you put the signature of a function in a header that can't be compiled, you have to compile also the body of that function in order to actually makes a runnable program with it (if you don't have a body somewhere you will get linker error)

Header.h

#pragma once

// by including this file you are actually promising that
// you will later add also the body of this function.
int function(int a); 

Source.cpp

#include <liba>
#include <libb>
#include "Header.h"

// by compiling this file AND linking into your binaries
// you satisfy the promise by providing the real function
int function(int a){
     return liba::fun(a)+libb::fun(b);
}

Is that usefull? Yes. Each file you include increase compile time and add dependencies to your code (if you change an header you have to recompile your program and there's the chance you also get compile errors, but if you keep stuff separed into 2 files you can have simpler header and you can change minor stuff inside your function without having to recompile a lot of files: in big projects compile time is a real issue).

The following 2 examples are equivalent (produce same assembly):

main.cpp

#include "Header.h"
int main(){

    return function(3);
}

You are including only 1 file


main2.cpp

#include "Source.cpp" //note source.cpp here!
int main(){

    return function(3);
}

You are including Source.cpp, Header.h, liba, libb (and possibly more) files, with result in 4x slower compiler time.


Of course headers also allows you to have a simpler life programming by avoiding duplicate definitions.

double inclusion

#include "Header.h"
#include "Header.h" //bad, but allowed!
int main(){

    return function(3);
}

double inclusion

#include "Source.cpp"
#include "Source.cpp" //error! redefinition (not compile)
int main(){

    return function(3);
}

double inclusion from different places

file1.cpp

#include "Source.cpp" // Bad! include Header.h

file2.cpp

#include "Source.cpp" //error symbol already define!
CoffeDeveloper
  • 7,961
  • 3
  • 35
  • 69