Is It a Good Practice to Place C++ Definitions in Header Files

Is it a good practice to place C++ definitions in header files?

Your coworker is wrong, the common way is and always has been to put code in .cpp files (or whatever extension you like) and declarations in headers.

There is occasionally some merit to putting code in the header, this can allow more clever inlining by the compiler. But at the same time, it can destroy your compile times since all code has to be processed every time it is included by the compiler.

Finally, it is often annoying to have circular object relationships (sometimes desired) when all the code is the headers.

Bottom line, you were right, he is wrong.

EDIT: I have been thinking about your question. There is one case where what he says is true. templates. Many newer "modern" libraries such as boost make heavy use of templates and often are "header only." However, this should only be done when dealing with templates as it is the only way to do it when dealing with them.

EDIT: Some people would like a little more clarification, here's some thoughts on the downsides to writing "header only" code:

If you search around, you will see quite a lot of people trying to find a way to reduce compile times when dealing with boost. For example: How to reduce compilation times with Boost Asio, which is seeing a 14s compile of a single 1K file with boost included. 14s may not seem to be "exploding", but it is certainly a lot longer than typical and can add up quite quickly when dealing with a large project. Header only libraries do affect compile times in a quite measurable way. We just tolerate it because boost is so useful.

Additionally, there are many things which cannot be done in headers only (even boost has libraries you need to link to for certain parts such as threads, filesystem, etc). A Primary example is that you cannot have simple global objects in header only libs (unless you resort to the abomination that is a singleton) as you will run into multiple definition errors. NOTE: C++17's inline variables will make this particular example doable in the future.

As a final point, when using boost as an example of header only code, a huge detail often gets missed.

Boost is library, not user level code. so it doesn't change that often. In user code, if you put everything in headers, every little change will cause you to have to recompile the entire project. That's a monumental waste of time (and is not the case for libraries that don't change from compile to compile). When you split things between header/source and better yet, use forward declarations to reduce includes, you can save hours of recompiling when added up across a day.

Is it ok to keep simple C++ method definitions inside of a header file?

@ReinstateMonica left a really good, succinct answer which I wholeheartedly agree with -- however I wanted to expand a little on it.

There is nothing inherently "good" or "bad" about defining functions inline in a header. There can be some cases where it is better to not define in a header since it may cause code-bloat; but this is something that would be determined after examining the product.

What matters most is that your code is easy to read and understand, because this increases maintainability -- it doesn't matter whether someone else will be the maintainer, or if its yourself a year from now.

In my experience working in large projects in C++, it becomes common to rely on header files as the form of "living documentation" in order to see what functionalities are introduced in any given header at a glance. Having code be simple to digest at a glance makes a huge difference on how quickly new code can be written using this API.

Function definitions that are written inline in the class definition can upset that readability by interrupting the natural flow. For example, consider the following two code snippets which are equivalent:

class Snake
{
public:

auto get_name() -> std::string
{
return m_name;
}

auto eat(const Fruit& fruit) -> void
{
..
// some really long definition
..
}

auto set_name(std::string name)
{
m_name = std::move(name);
}
};

Contrast this to the more succinct, but still inline:

class Snake
{
public:

auto get_name() const -> std::string;

auto eat(const Fruit& fruit) -> void;

auto set_name(std::string) -> void;
};

inline auto Snake::get_name() const -> void
{
return m_name;
}

inline auto Snake::eat(const Fruit& fruit) -> void
{
..
// some really long definition
..
}

inline auto Snake::set_name(std::string) -> void
{
m_name = std::move(name);
}

By simply relocating the definitions to the bottom and explicitly marking these inline, we get a quick topological view of the Snake class and all related actors and observer functionalities on it without having to scan between function definitions.

This will not change code generation in the slightest, but it can help to improve readability of the class by making it self-documenting -- which can be very useful if you work in a team environment, or ever have someone else come in to maintain your project.

Is it a good practice to define C++ functions inside header files?

If you want to use a function in multiple source files (or rather, translation units), then you place a function declaration (i.e. a function prototype) in the header file, and the definition in one source file.

Then when you build, you first compile the source files to object files, and then you link the object files into the final executable.


Example code:

  • Header file

      #ifndef FUNCTIONS_H_INCLUDED
    #define FUNCTIONS_H_INCLUDED

    int add(int a, int b); // Function prototype, its declaration

    #endif
  • First source file

      #include "functions.h"

    // Function definition
    int add(int a, int b)
    {
    return a + b;
    }
  • Second source file

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

    int main()
    {
    std::cout << "add(1, 2) = " << add(1, 2) << '\n';
    }

How you build it depends very much on your environment. If you are using an IDE (like Visual Studio, Eclipse, Xcode etc.) then you put all files into the project in the correct places.

If you are building from the command line in, for example, Linux or OSX, then you do:

$ g++ -c file1.cpp
$ g++ -c file2.cpp
$ g++ file1.o file2.o -o my_program

The flag -c tells the compiler to generate an object file, and name it the same as the source file but with a .o suffix. The last command links the two object files together to form the final executable, and names it my_program (that's what the -o option does, tells the name of the output file).

Is is a good practice to put the definition of C++ classes into the header file?

The answer depends on what kind of class you're creating.

C++'s compilation model dates back to the days of C, and so its method of importing data from one source file into another is comparatively primitive. The #include directive literally copies the contents of the file you're including into the source file, then treats the result as though it was the file you had written all along. You need to be careful about this because of a C++ policy called the one definition rule (ODR) which states, unsurprisingly, that every function and class should have at most one definition. This means that if you declare a class somewhere, all of that class's member functions should be either not defined at all or defined exactly once in exactly one file. There are some exceptions (I'll get to them in a minute), but for now just treat this rule as if it's a hard-and-fast, no-exceptions rule.

If you take a non-template class and put both the class definition and the implementation into a header file, you might run into trouble with the one definition rule. In particular, suppose that I have two different .cpp files that I compile, both of which #include your header containing both the implementation and the interface. In this case, if I try linking those two files together, the linker will find that each one contains a copy of the implementation code for the class's member functions. At this point, the linker will report an error because you have violated the one definition rule: there are two different implementations of all the class's member functions.

To prevent this, C++ programmers typically split classes up into a header file which contains the class declaration, along with the declarations of its member functions, without the implementations of those functions. The implementations are then put into a separate .cpp file which can be compiled and linked separately. This allows your code to avoid running into trouble with the ODR. Here's how. First, whenever you #include the class header file into multiple different .cpp files, each of them just gets a copy of the declarations of the member functions, not their definitions, and so none of your class's clients will end up with the definitions. This means that any number of clients can #include your header file without running into trouble at link-time. Since your own .cpp file with the implementation is the sole file that contains the implementations of the member functions, at link time you can merge it with any number of other client object files without a hassle. This is the main reason that you split the .h and .cpp files apart.

Of course, the ODR has a few exceptions. The first of these comes up with template functions and classes. The ODR explicitly states that you can have multiple different definitions for the same template class or function, provided that they're all equivalent. This is primarily to make it easier to compile templates - each C++ file can instantiate the same template without colliding with any other files. For this reason, and a few other technical reasons, class templates tend to just have a .h file without a matching .cpp file. Any number of clients can #include the file without trouble.

The other major exception to the ODR involves inline functions. The spec specifically states that the ODR does not apply to inline functions, so if you have a header file with an implementation of a class member function that's marked inline, that's perfectly fine. Any number of files can #include this file without breaking the ODR. Interestingly, any member function that's declared and defined in the body of a class is implicitly inline, so if you have a header like this:

#ifndef Include_Guard
#define Include_Guard

class MyClass {
public:
void DoSomething() {
/* ... code goes here ... */
}
};

#endif

Then you're not risking breaking the ODR. If you rewrite this as

#ifndef Include_Guard
#define Include_Guard

class MyClass {
public:
void DoSomething();
};

void MyClass::DoSomething() {
/* ... code goes here ... */
}

#endif

then you would be breaking the ODR, since the member function isn't marked inline and if multiple clients #include this file there will be multiple definitions of MyClass::DoSomething.

So to summarize - you should probably split up your classes into a .h/.cpp pair to avoid breaking the ODR. However, if you're writing a class template, you don't need the .cpp file (and probably shouldn't have one at all), and if you're okay marking every single member function of your class inline you can also avoid the .cpp file.

C Header Files - Good Practice

You normally distinguish between source and header files in the same way that Objective-C differentiates between implementation (.m) and interface (.h) files. Source files contain everything that may execute, header files contain enough information about symbols that other source files know how to communicate with that source file.

Header files often include other header files, so you'll see #include in both source and implementation files. #include operates exactly like #import except that it doesn't automatically check whether you've #included the same file twice. So C header files often look something like:

#ifndef __SOME_SYMBOL
#define __SOME_SYMBOL

... rest of header file here ...

#endif

Which has the same effect of ensuring the main body of the header file is included only once.

EDIT: more on this, as per request. Obviously you'd never do something like:

#include "File.h"
#include "File.h"

But you can easily end up with something like:

#include "FirstComplexThing.h"
#include "SecondComplexThing.h"

Where both FirstComplexThing.h and SecondComplexThing.h rely on something inside and hence #include SimpleThing.h. So you end up with SimpleThing.h #included twice, without making any sort of error or following any bad design pattern.

C compilers work just like Objective-C compilers — each source file is compiled on its own, in isolation, with no overview until the linker comes along. #include is a preprocessor directive that has the same logical effect as copying the contents of the named file and pasting them into your source file at that location, so if you end up the same file #included twice you'll probably end up with something like:

char *somePointer; // I'm from SimpleThing.h

... lots of other things ...

char *somePointer; // I'm from SimpleThing.h

And the compiler will stop with an error that the same thing is declared twice. #import in Objective-C avoids that by being shorthand for '#include, but only if you haven't already #included that file'. The C #ifndef/#define/#endif convention achieves the same thing as #import because the #ifndef/#endif pair say that the stuff in between should be passed on to the compiler if the nominated preprocessor symbol (__SOME_SYMBOL in my example; it tends to be a name derived from the name of that header file but exact conventions vary) hasn't been defined. It won't have been the first time the construct is encountered. Because it is defined inside the construct, it will have been when the same #ifndef is encountered the second time, so the stuff up to the matching #endif won't be passed on.

Although it's a question of style, it is very often the case that each C file has one H file that is directly connected to it.

There are no classes in C, obviously, but if you mean a construct like:

@class SomeClass;

@interface SomeOtherClass: NSObject
{
SomeClass *otherClass; // I can reference SomeClass without importing
// the interface file because I've declared the
// type above
}

- (void)whatever;
@end

That's actually the normal C distinction between declarations and definitions. You'll have a problem if you do something like:

struct SomeStruct;

struct SomeOtherStruct
{
struct SomeStruct otherStruct;
};

Because the compiler doesn't have enough information. It doesn't know how large SomeStruct should be, so it can't work out how SomeOtherStruct should be laid out. However, this is completely valid:

struct SomeStruct;

struct SomeOtherStruct
{
struct SomeStruct *otherStruct;
};

Because the size of a pointer is always known, irrespective of what it is pointing to. You'll often see that C libraries with opaque types describe those types by pointer only (sometimes to void *, but not always — e.g. stdio.h uses FILE *) or just give you an integer (including OpenGL, notably). So they ensure you've something that the compiler will know the size of without having to tell you what data they're associating with it or giving you any way to try to manipulate it yourself.

It's perfectly good practice to put pointers in the header file (assuming it's good practice to expose the thing globally, obviously). The same thing is often done in Objective-C, albeit for slightly different reasons, e.g.

// interface/header file

extern NSString *someGlobalIdentifier;

And:

// implementation/source file

NSString *someGlobalIdentifier = @"somethingOrOther";

In Objective-C that's because you can then test identity rather than always having to test equality, but basically the same rules apply to C with respect to it being normal to put the reference (be it a pointer or whatever) that represents a thing into the header and create or declare the thing in a source file. In fact, if you start putting declarations in the header file you'll end up with errors when the program comes to link because multiple source files will think they declare the thing.

include header file in this way, is this a good practice?

This is a decent and somewhat traditional way to use #include, if the array contents you're including has been automatically generated by some other process. (There are other ways of doing it, though, as I'll mention below.)

It is a classic tradeoff. Most of the time, a good rule that's worth following is that you should use the preprocessor only in straightforward, traditional ways, because when you start getting "tricky" it's all too easy to create an unholy mess.

But sometimes, you have an array whose contents you really want to generate using some automatic, external process. Copying and pasting the array definition into your source file would be tedious and error-prone, and you might have to keep redoing it as the array needed updating. Figuring out a way to automate the process is worth doing, and might even be worth violating the "no tricky preprocessor abuse" rule.

To make this work you will usually want to have some automatic process (perhaps an awk, sed, perl, or python script) that generates the included file with the correct syntax. If you're using make or something like it, you can have that step automatically performed whenever the actual source data for the array changes. For instance, in the example you gave, you might have an original "source file" tags.list containing lines like

html
head
title

and then in your Makefile use something like sed 's/.*/"&",' to create the include file with the proper string initializer syntax. That way you don't force the folks who are updating the list to remember to always use the right quotes and commas.

Also, as other commentators have suggested, you should probably give the file a name ending in something other than .h, to make clear that it's not an ordinary header file containing complete, valid C declarations. Better possibilities in this case would be .tab, .inc, or .arr.

With a little more work, though, you could avoid the "hack" and do things just about 100% conventionally. If you tweaked your script to add the line const char* kGumboTagNames[] = { at the beginning of the generated file, and }; at the end, you could give it a name ending in .c, and just compile it, rather than including it. (This approach, however, would involve its own tradeoff, in that it would constrain the array to being global, not static or local.)

Footnote: In some languages -- and even in C and C++, under some circumstances -- the comma is used as a separator, and you're not allowed to have one after the last element of a list. But in array initializers, you are allowed to have that trailing comma, and it turns out to be a pretty nice and useful freedom, precisely because it allows you to use straightforward techniques like the one described here, without the nuisance of having to insert an explicit extra step to get rid of the comma after the last element in the list.

Is it good practice to always include the standard headers?

Best practice is for each source file, whether it is a .c file or .h file, to include only the headers it needs. Doing so not only reduces compilation time but also serves to document the file's functionality.

Here are a few examples:

f1.c:

#include <stdio.h>

void print_int(int i)
{
printf("i=%d\n", i);
}

f2.c:

#include <stdlib.h>

void *safe_malloc(size_t size)
{
void *p = malloc(size);
if (!p) exit(1);
return p;
}

f3.c:

int sum(int a, int b)
{
return a+b;
}

The file f1.c only includes stdio.h because it does I/O but no other functions. Similarly, f2.c only includes stdlib.h because it uses functions defined there but does not use any I/O function, and f3.c doesn't include any headers because it doesn't need them.

If you think you've included unnecessary files, comment them out and compile with all warnings enabled to see which ones declare functions or variables you need.

Should the header file containing function prototypes be included in file with definitions?

Yes you should because it makes sure that the function signature is the same in the declaration and the definition. If they don't match the program won't compile. It is also a good idea to include myfunc.h before any other include file in myfunc.c. That way you will know that the header file is self-contained.



Related Topics



Leave a reply



Submit