Is Is a Good Practice to Put the Definition of C++ Classes into the Header 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.

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.

Why can classes be defined in header files?

If a header file has a definition of a class, for example ThisClass, and that header file is included in two other files for example a.cpp and b.cpp. Why wouldn't it violate the One Definition Rule?

Because, as you quoted, the one definition rule specifically allows for this.

How can it? Read on…

If a ThisClass object is created in either file, which definition would be called?

It doesn't matter, because the definitions are required to be absolutely, lexically identical.

If they're not, your program has undefined behaviour and you can expect all manner of weirdness to ensue.

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 it correct to define classes in header files?

Answer 1: Technically, yes. Your header file is defining your class, but that's OK. The definition of your class contains the declarations of several class methods, and that's what's important regarding the declare vs. define question.

Answer 2: Correct: Your test.hpp file contains declarations of the test methods, which are defined in your test.cpp file. It's ok for the header file to define a class that consists of method declarations. It doesn't really go against the rule; the rule is correct, but it mainly pertains to the methods of the class.

There are exceptions to this when considering things like inline methods and class forward declarations, but we don't need to worry about those here.

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 good practice to declare derivate classes in the same C++ header?

Two-file folders (like lib/implA/ImplA.h, lib/implA/ImplA.cpp) are unnecessary, for small projects people usually just put everything in lib/. If lib/ becomes too cluttered, put this whole hierarchy in lib/my_hierarchy/Base.h, lib/my_hierarchy/ImplA.cpp, etc. Maybe extract a logical subsystem instead of a hierarchy. Just keep reasonable folder sizes and some organized structure.

As for putting multiple declarations in the same header, it's your design choice. As far as I know, there's no single "best practice" in C++ regarding this. C++ doesn't enforce one class per file, like Java does. However, including a lot of classes in a single header means slightly longer compilation times for users, because that long header needs to be parsed in every .cpp file where it's #included. Usually people try to keep their headers minimal, but also provide a convenience "aggregate" header that includes all other headers (like bits/stdc++.h for the standard library). In your case, that would be:

// lib/lib.h

#include "my_hierarchy/Base.h"
#include "my_hierarchy/ImplA.h"
// etc.

So that users who don't mind longer compilation times can just #include <lib/lib.h> and have everything, while others can #include only classes they need.



Related Topics



Leave a reply



Submit