Defining constructor in header file vs. implementation (.cpp) file

85,809

Solution 1

One important point to note is that if a member function is defined inside a header file, it must be either inside the class body or it must be marked explicitly as inline. In other words, it is plain wrong to do this in a header file:

class A {
  public:
    A();
};

A::A() {
  // constructor body
}

The reason it's wrong is because it will make the compiler include the definition in each compilation unit, while it's obvious that any function must be defined only once. Here are correct ways to do the same thing:

class A {
  public:
    inline A();
};

inline A::A() {
  // constructor body
}

Or:

class A {
  public:
    inline A() { // inline isn't required here, but it's a good style
     // constructor body
    }
};

In both cases the constructor is inline. The only correct way to make it a regular out-of-line function would be to define it in the implementation file, not in the header. This is the most important difference between these two approaches.

Now, it is worth noting that inlining is an optimization. And as always with optimizations, they are best avoided until proved necessary. Among other problems inlining can lead to, there is the compatibility problem: if you change the body of a function that is not inlined, you only need to recompile the unit where it is defined, and everyone starts using the new implementation right away. With inlined functions, you need to recompile every unit that includes the relevant header, which can be a pain, especially if the header is used across different projects by different people.

In other words, use regular out-of-line definitions wherever possible until it is proved by profiling that a particular function call is a performance bottleneck. The only reasonable exception to this rule are trivial setters and getters, and even with them it is better to be careful - one day they may become non-trivial and it will mean a lot of recompilation and compatibility breaking.

Solution 2

Keep your headers free of implementations, unless you want the implementations to be inlined (e.g. trivial getters/setters). And unless they're templates, of course.

I see no reason to make an exception for constructors. Put them in the .cpp file.

Solution 3

Another note to consider: any changes to a header file require rebuilding all files that include that header file. Most build systems will rebuild source (*.cpp/.cc) files that depend on the modified header file.

If you change a method of a class defined in a header file, all sources files including the header file will be rebuilt. If you change a method in a source file, only the source file is rebuilt. This could be an issue for medium to larger projects.

To simplify the build process, most methods of a class should be defined in a source file. Small methods and other candidates for inlining should be defined in the header file.

Share:
85,809
Andrea
Author by

Andrea

Updated on December 30, 2021

Comments

  • Andrea
    Andrea over 2 years

    I can define the body of a class constructor in the class .h file or in the implementation file .cpp. These two styles are probably identical as far as the compiler is concerned within a specific project (project for me means DLL). Same applies to any member functions really: they can be defined in the header file or just declared there and then defined in the cpp file.

    However, I found that if I need to include such class header file(s) in different projects (meaning that ultimately the code that uses the header file ends up in a different DLL) then having the actual implementation in the header file causes some headaches at compilation (not at linking... I don't even get to that point). Why? Well I won't go too much in detail but the compiler obviously tries to resolve all the functions which might be defined in other header files etc., forcing the poor developer to start pulling in various header files etc.

    Isn't it always best to keep header files free of any implementation and just use them for 'declarations'? That would make it easier to include them in more than one projects w/o having to carry around a lot of extra junk.

    What is your opinion about this?

  • Nick Banks
    Nick Banks over 13 years
    There are even instances when you can put an implementation in the header. Say, if two different files both needed to include each other, that can't be done in the header, it must be done in the cpp.
  • user1703401
    user1703401 over 13 years
    The 'trivial' exception can just as easily be applied to a trivial constructor. No reason to treat them differently or to not have them inlined as well.
  • Deduplicator
    Deduplicator over 8 years
    Be aware that explicitly defaulted ctor/dtor/op= can only be trivial if implemented in-class.