30
votes

I have a c++ header file containing a class. I want to use this class in several projects, bu I don't want to create a separate library for it, so I'm putting both methods declarations and definitions in the header file:

// example.h
#ifndef EXAMPLE_H_
#define EXAMPLE_H_
namespace test_ns{

class TestClass{
public:
    void testMethod();
};

void TestClass::testMethod(){
    // some code here...
}

} // end namespace test_ns
#endif

If inside the same project I include this header from more than one cpp file, I get an error saying "multiple definition of test_ns::TestClass::testMethod()", while if I put the method definition inside the class body this does not happen:

// example.h
#ifndef EXAMPLE_H_
#define EXAMPLE_H_
namespace test_ns{

class TestClass{
public:
    void testMethod(){
        // some code here...
    }
};

} // end namespace test_ns
#endif

Since the class is defined inside a namespace, shouldn't the two forms be equivalent? Why is the method considered to be defined twice in the first case?

6

6 Answers

23
votes

These are not equivalent. The second example given has an implicit 'inline' modifier on the method and so the compiler will reconcile multiple definitions itself (most likely with internal linkage of the method if it isn't inlineable).

The first example isn't inline and so if this header is included in multiple translation units then you will have multiple definitions and linker errors.

Also, headers should really always be guarded to prevent multiple definition errors in the same translation unit. That should convert your header to:

#ifndef EXAMPLE_H
#define EXAMPLE_H

//define your class here

#endif
22
votes

Inside the class body is considered to be inline by the compiler. If you implement outside of body, but still in header, you have to mark the method as 'inline' explicitly.

namespace test_ns{

class TestClass{
public:
    inline void testMethod();
};

void TestClass::testMethod(){
    // some code here...
}

} // end namespace test_ns

Edit

For myself it often helps to solve these kinds of compile problems by realizing that the compiler does not see anything like a header file. Header files are preprocessed and the compiler just sees one huge file containing every line from every (recursively) included file. Normally the starting point for these recursive includes is a cpp source file that is being compiled. In our company, even a modest looking cpp file can be presented to the compiler as a 300000 line monster.

So when a method, that is not declared inline, is implemented in a header file, the compiler could end up seeing void TestClass::testMethod() {...} dozens of times in the preprocessed file. Now you can see that this does not make sense, same effect as you'd get when copy/pasting it multiple times in one source file. And even if you succeeded by only having it once in every compilation unit, by some form of conditional compilation ( e.g. using inclusion brackets ) the linker would still find this method's symbol to be in multiple compiled units ( object files ).

4
votes

Don't put a function/method definition in an header file unless they are inlined (by defining them directly in a class declaration or explicity specified by the inline keyword)

header files are (mostly) for declaration (whatever you need to declare). Definitions allowed are the ones for constants and inlined functions/methods (and templates too).

4
votes

Actually it is possible to have definitions in a single header file (without a separate .c/.cpp file) and still be able to use it from multiple source files.

Consider this foobar.h header:

#ifndef FOOBAR_H
#define FOOBAR_H

/* write declarations normally */
void foo();
void bar();

/* use conditional compilation to disable definitions when necessary */
#ifndef ONLY_DECLARATIONS
void foo() {
   /* your code goes here */
}
void bar() {
   /* your code goes here */
}
#endif /* ONLY_DECLARATIONS */
#endif /* FOOBAR_H */

If you use this header in only one source file, include and use it normally. Like in main.c:

#include "foobar.h"

int main(int argc, char *argv[]) {
    foo();
}

If there're other source files in your project which require foobar.h, then #define ONLY_DECLARATIONS macro before including it. In use_bar.c you may write:

#define ONLY_DECLARATIONS
#include "foobar.h"

void use_bar() {
    bar();
}

After compilation use_bar.o and main.o can be linked together without errors, because only one of them (main.o) will have implementation of foo() and bar().

That's slightly non-idiomatic, but it allows to keep definitions and declarations together in one file. I feel like it's a poor man's substitute for true modules.

1
votes

Your first code snippet is falling foul of C++'s "One Definition Rule" - see here for a link to a Wikipedia article describing ODR. You're actually falling foul of point #2 because every time the compiler includes the header file into a source file, you run into the risk of the compiler generating a globally visible definition of test_ns::TestClass::testMethod(). And of course by the time you get to link the code, the linker will have kittens because it will find the same symbol in multiple object files.

The second snippet works because you've inlined the definition of the function, which means that even if the compiler doesn't generate any inline code for the function (say, you've got inlining turned off or the compiler decides the function is too big to inline), the code generated for the function definition will be visible in the translation unit only, as if you'd stuck it in an anonymous namespace. Hence you get multiple copies of the function in the generated object code that the linker may or may not optimize away depending on how smart it is.

You could achieve a similar effect in your first code snippet by prefixing TestClass::testMethod() with inline.

-1
votes
//Baseclass.h  or  .cpp

#ifndef CDerivedclass
#include "Derivedclass.h"
#endif

or
//COthercls.h    or .cpp

#ifndef CCommonheadercls
#include "Commonheadercls.h"
#endif

I think this suffice all instances.