tags:

views:

8372

answers:

5

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?

+8  A: 

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 declard 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 ).

QBziZ
+3  A: 

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
workmad3
Thanks for pointing this out... I had forgot the include guards in the example (but not in the actual code).
Paolo Tedesco
+2  A: 

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).

PW
A: 

The Microsoft C++ compilers have '#pragma once' for this.

Steve Dunn
'#pragma once' is for multiple inclusion from a single compilation unit, this is a different problem.
otherwise known as 'non-portable header guard'
workmad3
+1  A: 

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.