120

Ok, not a C/C++ expert by any means, but I thought the point of a header file was to declare the functions, then the C/CPP file was to define the implementation.

However, reviewing some C++ code tonight, I found this in a class's header file...

public:
    UInt32 GetNumberChannels() const { return _numberChannels; } // <-- Huh??

private:
    UInt32 _numberChannels;

So why is there an implementation in a header? Does it have to do with the const keyword? Does that inline a class method? What exactly is the benefit/point of doing it this way vs. defining the implementation in the CPP file?

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • 8
    The function is _inlined_. – Some programmer dude Jan 25 '13 at 07:51
  • This is just one of the ways to declare an inline function in C++. – Michael Burr Jan 25 '13 at 07:52
  • And no it doesn't have to be const. – WhozCraig Jan 25 '13 at 07:52
  • 2
    RE the `const` qualifier; it only means that the method won't alter the object's state. – Michael Jan 25 '13 at 07:54
  • Does this mean any method body in a header file is automatically inlined, or does that have something to do with the `const` word? – Mark A. Donohoe Jan 25 '13 at 07:55
  • 4
    @Alex: you are incorrect that the compiler must inline the function. The compiler/linker have to deal with the multiple definitions (inline functions are not subject to the one definition rule). – Michael Burr Jan 25 '13 at 07:55
  • So you don't need to specify "inline" in this case? You can be sure the compiler is going to inline that function? – h4lc0n Jan 25 '13 at 07:56
  • OT: In the example given are there any benefits from inlining a return value, or is it pure lazyness we see here? – styken Jan 25 '13 at 07:57
  • 3
    @Alex no the compiler does not have to inlien it. It may inline it in some translation untis, but does not need to do so in all TU's. Yes, there are multiple definitions, but since the function is (implicitly) declared inline, the compiler marks the symbol if it does not inline it, and the linker knows it has to pick only one of the exported symbols. It's the same for template instantiations. – Arne Mertz Jan 25 '13 at 07:58
  • 1
    VC2010 will not inline such a function, IF its magic "inline budget" has been exhausted, for example. – ActiveTrayPrntrTagDataStrDrvr Jan 25 '13 at 08:00
  • Does this answer your question? [Is it a good practice to place C++ definitions in header files?](https://stackoverflow.com/questions/583255/is-it-a-good-practice-to-place-c-definitions-in-header-files) – Ciro Santilli OurBigBook.com May 13 '20 at 17:47

7 Answers7

176

Ok, not a C/C++ expert by any means, but I thought the point of a header file was to declare the functions, then the C/CPP file was to define the implementation.

The true purpose of a header file is to share code amongst multiple source files. It is commonly used to separate declarations from implementations for better code management, but that is not a requirement. It is possible to write code that does not rely on header files, and it is possible to write code that is made up of just header files (the STL and Boost libraries are good examples of that). Remember, when the preprocessor encounters an #include statement, it replaces the statement with the contents of the file being referenced, then the compiler only sees the completed pre-processed code.

So, for example, if you have the following files:

Foo.h:

#ifndef FooH
#define FooH

class Foo
{
public:
    UInt32 GetNumberChannels() const;

private:
    UInt32 _numberChannels;
};

#endif

Foo.cpp:

#include "Foo.h"

UInt32 Foo::GetNumberChannels() const
{
    return _numberChannels;
}

Bar.cpp:

#include "Foo.h"

Foo f;
UInt32 chans = f.GetNumberChannels();

The preprocessor parses Foo.cpp and Bar.cpp separately and produces the following code that the compiler then parses:

Foo.cpp:

class Foo
{
public:
    UInt32 GetNumberChannels() const;

private:
    UInt32 _numberChannels;
};

UInt32 Foo::GetNumberChannels() const
{
    return _numberChannels;
}

Bar.cpp:

class Foo
{
public:
    UInt32 GetNumberChannels() const;

private:
    UInt32 _numberChannels;
};

Foo f;
UInt32 chans = f.GetNumberChannels();

Bar.cpp compiles into Bar.obj and contains a reference to call into Foo::GetNumberChannels(). Foo.cpp compiles into Foo.obj and contains the actual implementation of Foo::GetNumberChannels(). After compiling, the linker then matches up the .obj files and links them together to produce the final executable.

So why is there an implementation in a header?

By including the method implementation inside the method declaration, it is being implicitly declared as inlined (there is an actual inline keyword that can be explicitly used as well). Indicating that the compiler should inline a function is only a hint which does not guarantee that the function will actually get inlined. But if it does, then wherever the inlined function is called from, the contents of the function are copied directly into the call site, instead of generating a CALL statement to jump into the function and jump back to the caller upon exiting. The compiler can then take the surrounding code into account and optimize the copied code further, if possible. 

Does it have to do with the const keyword?

No. The const keyword merely indicates to the compiler that the method will not alter the state of the object it is being called on at runtime.

What exactly is the benefit/point of doing it this way vs. defining the implementation in the CPP file?

When used effectively, it allows the compiler to usually produce faster and better optimized machine code.

Greenbeard
  • 508
  • 5
  • 14
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 3
    Does this mean, by your explanation, that you can declare a class directly in your CPP file, and even declare the contents of the member functions within the braces wrapping that class declaration, so you wouldn't have to use the :: syntax outside of it? (I understand that is not considered good coding. I'm asking only if it's valid coding.) And to that effect, would that mean that all members would be inlined, or at least marked to be? (And is there something that you can say *don't* inline this?) – Mark A. Donohoe Jan 25 '13 at 08:32
  • 1
    @MarqueIV What you describe is technically possible, but it would prevent you from using that class anywhere outside of the compilation unit defined by the .cpp file (essentially, the cpp file itself, ubless you #include it in other files. Which is a huge no-no!). You could, however, still use pointers or references to the class (just never dereference them or access members via the pointers or references), if you forward-declare the class in other files. – Agentlien Jan 25 '13 at 08:55
  • Yeah, I know I couldn't use them that way. This was more of a 'can you do it' kinda thing, not a 'should you do it'. I was more asking if it would successfully compile, which it would. Anyway, you got the answer because of the detail and you also being the only person to ref the const keyword in your answer (at least when I approved it anyway.) Thanks! :) – Mark A. Donohoe Jan 25 '13 at 09:09
  • The header guard in the example does not seem to do anything. Could you explain when header guard will take effect and when not? – Helin Wang Jan 31 '17 at 15:49
  • @RemyLebeau Thanks! Another question, if the implementation is in header file with header guard. And that header file got used by a shared library project and main project. And the main project uses the library project. During linking, will linker complain about the same function got defined twice (duplicate symbol)? – Helin Wang Jan 31 '17 at 16:52
  • @HelinWang: it might, yes. In which case, the implementation belongs in the shared library only, and then have the main project link to it. – Remy Lebeau Jan 31 '17 at 21:09
  • FYI for readers) In C++, the `inline` keyword not only means the conventional meaning of the "inline"(related to optimization, but in C++ it's up to the compiler), but *also* means that the definition can be in multiple translation units(in other words, it is treated as an exception in the *'one definition rule'*). – starriet Mar 27 '23 at 06:10
52

It is perfectly valid to have an implementation of a function in a header file. The only issue with this is breaking the one-definition-rule. That is, if you include the header from multiple other files, you will get a compiler error.

However, there is one exception. If you declare a function to be inline, it is exempt from the one-definition-rule. This is what is happening here, since member functions defined inside a class definition are implicitly inline.

Inline itself is a hint to the compiler that a function may be a good candidate for inlining. That is, expanding any call to it into the definition of the function, rather than a simple function call. This is an optimization which trades the size of the generated file for faster code. In modern compilers, providing this inlining hint for a function is mostly ignored, except for the effects it has on the one-definition-rule. Also, a compiler is always free to inline any function it sees fit, even if it has not been declared inline (explicitly or implicitly).

In your example, the use of const after the argument list signals that the member function does not modify the object on which it is called. In practice, this means that the object pointed to by this, and by extension all class members, will be considered const. That is, trying to modify them will generate a compile-time error.

Agentlien
  • 4,996
  • 1
  • 16
  • 27
  • 2
    "since member functions defined inside a class definition are implicitly inline." Valuable info. Did not know that. But what about that `const` word? – Mark A. Donohoe Jan 25 '13 at 08:09
  • 6
    Thanks for mentioning the [one-definition-rule](http://en.wikipedia.org/wiki/One_Definition_Rule)! – dubbaluga Aug 19 '14 at 22:21
12

It is implicitly declared inline by virtue of being a member function defined within the class declaration. This does not mean the compiler has to inline it, but it means you won't break the one definition rule. It is completely unrelated to const*. It is also unrelated to the length and complexity of the function.

If it were a non-member function, then you would have to explicitly declare it as inline:

inline void foo() { std::cout << "foo!\n"; }

* See here for more on const at the end of a member function.

Community
  • 1
  • 1
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • By One Definition Rule, do you mean here that if defined in the header, a function cannot possibly be defined in another cpp file hence? – piedpiper Sep 12 '19 at 21:39
  • @ashu it is more than "another cpp file", it is in the entire program : https://en.wikipedia.org/wiki/One_Definition_Rule In my case I had compilation error (`multiple definition`), and adding explicitly the `inline` keyword solved it. – jeannej Apr 27 '21 at 14:05
5

Even in plain C, it is possible to put code in a header file. If you do it, you usually need to declare it static or else multiple .c files including the same header will cause a "multiply defined function" error.

The preprocessor textually includes an include file, so the code in an include file becomes part of the source file (at least from the compiler's point of view).

The designers of C++ wanted to enable object-oriented programming with good data hiding, so they expected to see lots of getter and setter functions. They didn't want an unreasonable performance penalty. So, they designed C++ so that the getters and setters could not only be declared in the header but actually implemented, so they would inline. That function you showed is a getter, and when that C++ code is compiled, there won't be any function call; code to fetch out that value will just be compiled in place.

It is possible to make a computer language that doesn't have the header file/source file distinction, but just has actual "modules" that the compiler understands. (C++ didn't do that; they just built on top of the successful C model of source files and textually included header files.) If source files are modules, it would be possible for a compiler to pull code out of the module and then inline that code. But the way C++ did it is simpler to implement.

steveha
  • 74,789
  • 21
  • 92
  • 117
  • I don't think `static` will prevent multiple definitions. I think you meant "header guards" were needed. – Michael Ansolis May 23 '23 at 23:02
  • `static` should be `inline`? – fstang Jul 04 '23 at 09:47
  • @MichaelAnsolis I just tested it and confirmed that `static` does work as I claimed. The C compiler will compile the `static` function from the header file, and the visibility of that function will be strictly within the C file that included that header. What I believe happens is that the name of the function is mangled to be specific to that C source file, and the name isn't published for the linker to see, so no other code can make an `extern` declaration to use that function. It's not a great way to do it because you get multiple copies of the function, with no benefit. – steveha Jul 04 '23 at 23:12
  • 1
    @fstang If you are using a a modern C compiler you will have access to the `inline` keyword. This would have the effect that the code of the function is compiled in where the function call was placed. This would also avoid the multiply-defined error. If you don't have either `static` or `inline` an externally-visible function would be defined. With `inline` you don't get the function; with `static` the function isn't externally-visible. – steveha Jul 04 '23 at 23:17
3

As far as I know, there are two kinds of methods, which can be safely implemented inside the header file.

  • Inline methods - their implementation is copied to places, where they are used, so there is no problem with double-definition linker errors;
  • Template methods - they are actually compiled at the moment of template instantiation (eg. when someone inputs a type in place of template), so again there is no possibility of double-definition problem.

I believe, your example fits the first case.

Spook
  • 25,318
  • 18
  • 90
  • 167
1

C++ standard quotes

The C++17 N4659 standard draft 10.1.6 "The inline specifier" says that methods are implicitly inline:

4 A function defined within a class definition is an inline function.

and then further down we see that inline methods not only can, but must be defined on all translation units:

6 An inline function or variable shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (6.2).

This is also explicitly mentioned in a note at 12.2.1 "Member functions":

1 A member function may be defined (11.4) in its class definition, in which case it is an inline member function (10.1.6) [...]

3 [ Note: There can be at most one definition of a non-inline member function in a program. There may be more than one inline member function definition in a program. See 6.2 and 10.1.6. — end note ]

GCC 8.3 implementation

main.cpp

struct MyClass {
    void myMethod() {}
};

int main() {
    MyClass().myMethod();
}

Compile and view symbols:

g++ -c main.cpp
nm -C main.o

output:

                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 W MyClass::myMethod()
                 U __stack_chk_fail
0000000000000000 T main

then we see from man nm that the MyClass::myMethod symbol is marked as weak on the ELF object files, which implies that it can appear on multiple object files:

"W" "w" The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the symbol is determined in a system-specific manner without error. On some systems, uppercase indicates that a default value has been specified.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
0

Keeping the implementation in the class header file works, as I'm sure you know if you compiled your code. The const keyword ensures you don't change any members, it keeps the instance immutable for the duration of the method call.

Jonas Byström
  • 25,316
  • 23
  • 100
  • 147