|
Function definitions in .h or .cpp files? |
Bob Keane
Member #7,342
June 2006
|
While reading up on c++ programming, I found a programming problem which specified putting a function definition in a .cpp file. I remember a previous chapter specified putting the definitions in a .h file, so I was wondering: What is the difference between using a .cpp file as opposed to a .h file for function definitions? Is one preferred over the other? Are there any advantages/disadvantages? By reading this sig, I, the reader, agree to render my soul to Bob Keane. I, the reader, understand this is a legally binding contract and freely render my soul. |
gnolam
Member #2,030
March 2002
|
Function declarations go in the header, function definitions (of which there must never be more than one for each unique function) go in the .cpp file. Never confuse the two. -- |
bamccaig
Member #7,536
July 2006
|
Declaration: void f(void); Definition: void f(void) { std::cout << std::endl; } Definitions always go in source files (i.e., .c, .cpp). Declarations for things you want exposed to the "world" go in header files (i.e, .h, .hpp) so that they can be declared to the world by #include'ing them. The declaration says something exists. The definition says what that something is. The definition is used to generate the appropriate assembly. The declaration is used to tell parts that don't yet know about the definition[1] that something will exist when the linking happens. The linker ties the definition to the calls. The compiler only cares that something is declared, but the linker requires things to be defined. References
-- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Bob Keane
Member #7,342
June 2006
|
I understand putting the declarations in a separate file, but does it make a difference whether the definitions are in a .cpp or .h file? I was able to compile and run a program with the definitions in a .h file. Is it just semantics? Gnolam said: function definitions (of which there must never be more than one for each unique function) What about operator overloading, or am I talking apples to oranges? By reading this sig, I, the reader, agree to render my soul to Bob Keane. I, the reader, understand this is a legally binding contract and freely render my soul. |
Tobias Dammers
Member #2,604
August 2002
|
Bob Keane said: What about operator overloading, or am I talking apples to oranges? Functions in the sense discussed here are individual overloads for a function name. In other words, a function is identified by its full signature, not just the name. In the following example, four functions are declared, all by the name 'foo': class Bar { void foo(); void foo() const; } void foo(const Bar& the_bar); int foo(const Bar& bar_one, const Bar& bar_two); As far as the compiler is concerned, those are four different functions. --- |
bamccaig
Member #7,536
July 2006
|
Bob Keane said: I understand putting the declarations in a separate file, but does it make a difference whether the definitions are in a .cpp or .h file? I was able to compile and run a program with the definitions in a .h file. Is it just semantics? The preprocessor generates a complete source file by reading in the original source file and processing each directive (lines whose first non-whitespace character is #). The #include directive essentially searches the include path for a matching file and inserts the contents in place of the directive. It would be equivalent for you to just copy/paste the header file in place of the #include directive. Each source file is compiled separately. The linker puts it all together in the end. Definitions can only exist one time for an entire program (the linker needs one and only one definition of everything). The whole point of header files is to allow them to be included in multiple source files during compilation so that source files can know about and use code from other files. In other words, definitions should never (Edit: evidently, inline functions are an exception; one I'm clearly unfamiliar with) go in header files, but there's nothing intelligently enforcing that it can't be done. The compiler doesn't even know about header files[1]. Header files are dealt with by the preprocessor. The compiler just takes the source code of a single file after being preprocessed and compiles it. 1#ifndef BAD_HPP
2 #define BAD_HPP
3
4 #include <iostream>
5
6void f1(void) 7{ 8 std::cout << std::endl; 9} 10
11#endif
1#include <bad.hpp>
2
3int main(int argc, char * argv[])
4{
5 f1();
6
7 return 0;
8}
The above works because bad.hpp is only ever included once so f1 will only ever be defined once. What the compiler actually sees is something like this: 1void f1(void)
2{
3 std::cout << std::endl;
4}
5
6int main(int argc, char * argv[])
7{
8 f1();
9
10 return 0;
11}
(For simplicity, I've excluded the declarations that would have come from the standard header files... If you want to see this in action though, pass the -E option to GCC) However, if the program had another part to it, that might not be the case. Imagine we had two extra files: 1#ifndef GOOD_HPP
2 #define GOOD_HPP
3
4 #include <bad.hpp>
5 #include <string>
6
7void f2(const std::string);
8
9#endif
1#include <good.hpp>
2
3void f2(const std::string)
4{
5 f1();
6}
This won't work anymore. The reason is, when main.cpp is compiled, a definition for f1 is found (which was inserted by the preprocessor from bad.hpp). Then, when good.cpp is compiled, a definition for f1 is found (again, inserted by the preprocessor from bad.hpp). The compiler is only looking at one source file at a time so the compilation goes fine. However, when the linker tries to put it all together, it finds multiple definitions of f1(void) and returns an error. References
-- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Thomas Fjellstrom
Member #476
June 2000
|
You CAN put functions in headers, but it may cause multiple redefinition warnings if you include the header in more than one source file. The COMMON use of functions in headers are inline functions or methods. -- |
ReyBrujo
Moderator
January 2001
|
Bob Keane said: I understand putting the declarations in a separate file, but does it make a difference whether the definitions are in a .cpp or .h file? Also, if you use build systems like makefile, they are able to create a dependency tree to know if a file must be compiled. If you place code in the header, whenever you need to change the code of the function you need to recompile all the files that are using the header. However, if you place the definition in the source file and the declaration in the header, only the source file needs to be recompiled. When you have a small project it is not much of a difference. But at work projects with over 10,000 objects require 30-45 minutes to compile (including 15 of linking). -- |
gnolam
Member #2,030
March 2002
|
Thomas Fjellstrom said: The COMMON use When it comes to programming rules like one (e.g. "Declarations always go in header files, definitions always go in source files"), the law to abide by is simple: If you don't know exactly what the exceptions to the rule are, follow the rule to the letter like it had no exceptions. -- |
Bob Keane
Member #7,342
June 2006
|
I was able to compile and run a program that has a function definition separate from the main.cpp without #including it. I am using an ide btw. The definition file was in the same directory as main.cpp. How does that work, does the compiler compile all .cpp files in the project and the linker link them automatically? By reading this sig, I, the reader, agree to render my soul to Bob Keane. I, the reader, understand this is a legally binding contract and freely render my soul. |
Thomas Fjellstrom
Member #476
June 2000
|
Bob Keane said: How does that work, does the compiler compile all .cpp files in the project and the linker link them automatically? If you added the cpp files to your project the IDE will compile them all, and link them together. Quote: I was able to compile and run a program that has a function definition separate from the main.cpp without #including it. As long as you declare a function before using it, you won't have any problems. If you DON'T pre declare the function in some header or in each source file that uses it (which is the same thing as including a header with the declaration), the compiler may get things wrong. In fact it probably will, unless its a function that returns void, and takes unknown arguments. -- |
LennyLen
Member #5,313
December 2004
|
Bob Keane said: I was able to compile and run a program that has a function definition separate from the main.cpp without #including it. I am using an ide btw. The definition file was in the same directory as main.cpp. How does that work, does the compiler compile all .cpp files in the project and the linker link them automatically? Was the definition in a .h file or a .cpp file? If the latter, was that .cpp file in your project? With every IDE I've ever used, all .cpp (or .c) files in the project will always be compiled and linked.
|
ImLeftFooted
Member #3,935
October 2003
|
I put all my function definitions in .cpp files and implementation in .h files. I only include .cpp files and I compile the .h files. Gcc gets a little funny so you have to explicit set the language when you're compiling. Files: main.h -> this includes myclass.cpp myclass.h -> this includes myclass.cpp myclass.cpp g++ -x c++ *.h -o mygame.exe The fact that gcc does not support this natively indicates a dire need for better standards relating to header files and their usage. |
Bob Keane
Member #7,342
June 2006
|
LennyLen said: Was the definition in a .h file or a .cpp file? The definition was in a separate .cpp file. The file was saved in the same folder as main.cpp. LennyLen said: With every IDE I've ever used, all .cpp (or .c) files in the project will always be compiled and linked. So the ide assumes all .cpp files in the working directory are part of the project? Wouldn't that cause a problem with stray files? For example, if I copied a folder to create a new project and forgot to delete a .cpp file? By reading this sig, I, the reader, agree to render my soul to Bob Keane. I, the reader, understand this is a legally binding contract and freely render my soul. |
LennyLen
Member #5,313
December 2004
|
Bob Keane said: So the ide assumes all .cpp files in the working directory are part of the project? It shouldn't. It should only compile/link files that you've added to the project. Which IDE are you using?
|
decepto
Member #7,102
April 2006
|
Bob Keane said: What about operator overloading, or am I talking apples to oranges? I'm going to be bold here. There's never any good reason to use operator overloading. -------------------------------------------------- |
LennyLen
Member #5,313
December 2004
|
decepto said: I'm going to be bold here. There's never any good reason to use operator overloading.
That is fairly bold. I've always thought that the way + is overloaded for strings was a good use. name = "Jack" + " " + "Smith";
|
bamccaig
Member #7,536
July 2006
|
Operator overloading is very reasonable. Consider the amount of time it takes for a human to interpret... a = b + c * d / e % f; ...versus... a = b.add(c.mult(d).div(e).mod(f)); Operators hopefully make code compact without losing meaning (like using abbreviations or slang). I've actually recently been contemplating a language that supported custom keywords, albeit there are drawbacks to consider and solve first (name collisions perhaps being the most obvious). Operator overloading doesn't suffer from that though. The only potential flaw is misuse, but that can be true of anything (i.e., an add method that actually just prints to stdout). -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Tobias Dammers
Member #2,604
August 2002
|
Bob Keane said: I was able to compile and run a program that has a function definition separate from the main.cpp without #including it. I am using an ide btw. The definition file was in the same directory as main.cpp. How does that work, does the compiler compile all .cpp files in the project and the linker link them automatically?
The compiler itself compiles exactly what you tell it to. Taking gcc as an example, the following command: If you don't #include the header, the compiler will not know the declarations of the functions inside it. However, some compilers may guess when they encounter something they don't know (this is called 'implicit declaration'). When an unknown identifier is used as a function, it is assumed to be called correctly (i.e. with the correct argument structure), and return an int. Similarly, undefined variables are assumed to be ints (in plain C at least). --- |
gnolam
Member #2,030
March 2002
|
Tobias Dammers said: When an unknown identifier is used as a function, it is assumed to be called correctly (i.e. with the correct argument structure), and return an int. Similarly, undefined variables are assumed to be ints (in plain C at least). Thankfully, implicit int was removed completely in C99. -- |
Tobias Dammers
Member #2,604
August 2002
|
I should have added that anyone who knowingly uses this brain-damaged behaviour should be punished. Hard. --- |
|