• Create Account

Help me understand .cpp vs .h in C++

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

20 replies to this topic

#1chiranjivi  Members   -  Reputation: 208

Like
6Likes
Like

Posted 24 March 2013 - 08:15 AM

Having trouble getting my head around the concept of separating code into .cpp and .h files. I've been coding in C++ for a while now and have never done anything other than just stick all code into header files, which I guess I'm going to have to grow out of eventually.

Here's an example of the problems I'm having. This is the level class for a toy game. From what I have been reading I believe I'm supposed to split it into level.h and level.cpp something like this:

level.h
#ifndef LEVEL_H_INCLUDED
#define LEVEL_H_INCLUDED

class Level {
public:
Level();

int width, height;
int map_grid[20][20];

void display_grid();
};

#endif // LEVEL_H_INCLUDED


level.cpp
#include "level.h"

Level::Level() {
width = height = 20;
for ( int i = 0; i != width; ++i ) for ( int j = 0; j != height; ++j ) {
if ( i == 0 || i == width-1 || j == 0 || j == height-1 ) {
map_grid[j][i] = 1;
} else {
map_grid[j][i] = 0;
}
}
}

void Level::display_grid() {
for (int i = 0; i != width; ++i ) for ( int j = 0; j != height; ++j ) {
if ( map_grid[j][i] == 1 ) draw_sprite( i*64, j*64, block_blue, screen );
}
}


When I do this, the compiler doesn't like level.cpp, saying 'draw_sprite', 'block_blue' and 'screen' have not been declared. 'draw_sprite' is a function and 'block_blue' and 'screen' are SDL_Surfaces, and they have been declared in an earlier include ("sdl_functions.h"). Attempting to #include sdl_functions.h again at the start of level.cpp prompts the compiler to get angry about me trying to re-declare a load of stuff.

What I don't understand is that if I just mash level.h and level.cpp together into a single header file:

level.h
#ifndef LEVEL_H_INCLUDED
#define LEVEL_H_INCLUDED

class Level {
public:
Level();

int width, height;
int map_grid[20][20];

void display_grid();
};

Level::Level() {
width = height = 20;
for ( int i = 0; i != width; ++i ) for ( int j = 0; j != height; ++j ) {
if ( i == 0 || i == width-1 || j == 0 || j == height-1 ) {
map_grid[j][i] = 1;
} else {
map_grid[j][i] = 0;
}
}
}

void Level::display_grid() {
for (int i = 0; i != width; ++i ) for ( int j = 0; j != height; ++j ) {
if ( map_grid[j][i] == 1 ) draw_sprite( i*64, j*64, block_blue, screen );
}
}

#endif // LEVEL_H_INCLUDED


...then it works?

Obviously there are huge gaps in my knowledge as to how header and source files are supposed to interact (as well as in many other areas ), so please go easy on me for asking a very stupid question. Thanks.

#2kubalie  Members   -  Reputation: 130

Like
6Likes
Like

Posted 24 March 2013 - 09:08 AM

Hey. So the purpose of seperating your code in headers and source (.h/.cpp) is code organization and also lets say you are developing a library to distribute to other developers. So you can make a lib file with a header and people would know what functions are available to them and your logic is hidden.

#3Alpheus  Crossbones+   -  Reputation: 6463

Like
4Likes
Like

Posted 24 March 2013 - 09:21 AM

#ifndef LEVEL_H_INCLUDED
#define LEVEL_H_INCLUDED

#include "sdl_functions.h"

class Level {
public:
Level();

int width, height;
int map_grid[20][20];

void display_grid();
};

#endif // LEVEL_H_INCLUDED


Separate your .h and .cpp files again and use the version I have posted. Does this help any? Or change any of the errors?

External Articulation of Concepts Materializes Innate Knowledge of One's Craft and Science

Super Mario Bros clone tutorial written in XNA 4.0 [MonoGame, ANX, and MonoXNA] by Scott Haley

If you have found any of the posts helpful, please show your appreciation by clicking the up arrow on those posts

Spoiler

#4Poigahn  Crossbones+   -  Reputation: 564

Like
-11Likes
Like

Posted 24 March 2013 - 09:24 AM

TTFN

Edited by Poigahn, 25 March 2013 - 02:53 PM.

Your Brain contains the Best Program Ever Written : Manage Your Data Wisely !!

#5Juliean  GDNet+   -  Reputation: 5379

Like
3Likes
Like

Posted 24 March 2013 - 09:28 AM

class Level {
public:
Level();

public :

class Level{....

This makes this Class Public to the calling program

class Level{

public:

Level()

only makes this class public inside the current header file, as in your second example.

This is simply wrong. public, private and protected only take effect inside classes and/or structs, a class is always "public" where you include it. So he got that one right.

@Topic:

    #ifndef LEVEL_H_INCLUDED
#define LEVEL_H_INCLUDED

#include "sdl_functions.h"

...

#endif // LEVEL_H_INCLUDED


You can simply put "#pragma once" at the top of your header file. Make sure everything is included beyond that, then your errors should be solved.

Edited by The King2, 24 March 2013 - 09:31 AM.

#6Cornstalks  Crossbones+   -  Reputation: 7020

Like
6Likes
Like

Posted 24 March 2013 - 09:59 AM

class Level {
public:
Level();

public :
class Level{....

This makes this Class Public to the calling program

class Level{
public:
Level()

only makes this class public inside the current header file, as in your second example.

That's not how C++ works, and that code won't even compile. It will break things. The original code is correct (in this regard). In C++, #include statements literally copy and paste the included file into the current file when compiling, so there's no real distinction between "what's public in a header file and what's not" seeing as the file's contents are literally copy and pasted in a #include statement.

It sounds like the problem is in "sdl_functions.h" and judging by the error, it doesn't sound like the real problem is multiple declarations, but instead multiple definitions (there's a subtle difference, and it's possible the compiler's error message isn't too clear).

Rule 1: Don't define global functions or variables in a header. Define them in a single .cpp file (or spread across multiple .cpp files, but only give one definition to each global function and variable).

Rule 2: If you have global functions or variables defined in one .cpp, and you want to use them in another .cpp, create a header and declare the global functions or variables extern. You can google declaration vs definition (and what forward declaration is) for more information.

Rule 3: In some cases, you don't want to follow rules #1 and #2, and in these special cases you should mark the global functions or variables as static (or put them in an unnamed/anonymous namespace) and define in a .h file that you include in other .cpp files.

There are a lot of details I'm leaving out and I'm trying to make these rules very simple (and I don't have time right now). I wrote a blog post about the static modifier once, and I'm guessing that the last case for the static modifier that I talked about applies here.

Edited by Cornstalks, 24 March 2013 - 10:54 AM.

[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#7Poigahn  Crossbones+   -  Reputation: 564

Like
-1Likes
Like

Posted 24 March 2013 - 10:02 AM

TTFN

Edited by Poigahn, 25 March 2013 - 02:54 PM.

Your Brain contains the Best Program Ever Written : Manage Your Data Wisely !!

#8Cornstalks  Crossbones+   -  Reputation: 7020

Like
9Likes
Like

Posted 24 March 2013 - 10:13 AM

Like I said I was not up on my CPP and also asked for help in clarification,  So I guess by trying and asking fo help you get knocked down.

This is why people do not try.

Which is unfortunate. Taking a little risk and being wrong can be a great learning experience (if you take the criticism right, and of course, if your mistake doesn't kill people ).

I have been out of CPP for awhile and I am trying to get back into it.  I realize newer versions of CPP have changed and so has the format.

C++ has been like this for decades... Granted, C++ wasn't standardized as a language until 1998, so if you learned C++ before 1998, it's possible you learned someone's custom flavor of C++.

When I learned,  each function had to be listed at the beginning of the program like such.

Int DiceRoll()

Void Showresults()

Sounds more like C than C++.

Now I am not seeing that in the code.  So I was incorrect, again stating that I was uncertain of that.  Hoping for help myself not votes down.

THanks!!

I don't want to derail the thread (and I fear I may be doing that!), but I'll give a little insight here. I voted your post down because it's technically incorrect. Not because I hate your or think you're stupid for trying (I don't!). I just want to make it clear to anyone quickly skimming this thread that the technical accuracy of that post is... lacking. I don't think people will gang up on you and give you a million -1 votes, but I might suggest editing your post and adding a strikethrough all the part about "public" and adding a note that yes, you were mistaken (I've done this when I'm wrong on something, and I've never been "gang downvoted" after doing so (though I usually get one or two -1 votes so that it's clear to the people of the internet that something was wrong)).

[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#9powly k  Members   -  Reputation: 657

Like
2Likes
Like

Posted 24 March 2013 - 01:09 PM

This is a hard topic unless you know just a bit about what's going on behind the scenes when compiling. What happens is basically the compiler looks at what .cpp files you have in the project (or folder or the ones you tell it to compile, this depends), turns each one of them into an object file (.obj with visual studio, .a and .o also exist with some compilers IIRC) and it's done. After this the linker jumps onto the scene and starts looking at what pieces of code it needs to make the program into an actual executable - this is why you, for example, tell the linker, not the compiler, where the execution starts. Here you can have so called statically linked libraries too, that's the list of .lib files you feed to your linker. The most usual linker errors occur when you either forget to link against something or have multiple definitions - the same variable or function exists in several object files and the linker can't figure out which one to use.

Now, you might notice I didn't mention headers at all. This is because a header is never compiled itself, it's just included into (usually several) other files. So if your header has a global variable definition (for example "int x = 5;"), it'll be in all of the .cpp files and the linker won't like this. So you want your headers to only hold declarations, not definitions. int foo(int x); is okay, int foo(int x){return x*2+1;} isn't. Unless it's code you only use in one file, though then you don't even have to keep it in the header at all.

Edited by powly k, 24 March 2013 - 01:11 PM.

#10giant_city  Members   -  Reputation: 209

Like
4Likes
Like

Posted 24 March 2013 - 02:41 PM

This is kind of a tricky thing to explain, but when you understand it, you will understand how executables and libraries work much better. The solution to your problem has already been posted, but if you want the long explanation of what's going on, here it is! The main thing you need to understand is how the compiler and the linker work together. Pay close attention to the words declare and define.

The compiler turns all your source code into object files (as mentioned earlier), which contain actual executable code (basically). For each method, a callable segment of machine code is created. However, within most functions, there are also calls to other functions. Since there is no need to reproduce the called function (inline functions excepted), a dependency is created on the code of the called function. So for instance, the first two lines of following function will be compiled, but one external dependency exists.

foo.cpp

void foo() {
int x = 0;
x = x + 2;
bar(x);
}

You will have a compilation error if this was the entirety of the source file, since bar(int) is never declared or defined. So, either you must define it in this source file, or declare it with a prototype with the signature bar(int). Here's where the linker comes in:

Suppose bar(int) is defined in another source file. You need a way of telling the compiler not to worry about it right now, but that it can count on it existing when the final executable is being built (it's the linker's job to connect unresolved functions/variables to their instantiations), so that's what a header file does:

bar.h

#pragma once
void bar(int x);

We have now declared bar(int). We now need to look at foo.h:

#pragma once
#include "bar.h"
void foo();

And of course we need to include foo.h in foo.c (I didn't show that earlier since I wanted to make sure that it would be understood that the compiler would have no way of knowing that bar(int) existed at that point). So, what happens is that the compiler is told: 'there will be a method with the signature bar(int)', so you can make that call in the code. Now, as far as we've gotten, we still haven't defined bar(int), so if we tried to build as is, the compilation process would go fine, but we would get an unresolved external symbol: ...bar(int) from the linker. The reason is that the linker's job is to find all the unresolved calls and resolve them. This means: find the compiled code for bar(int) and actually jump to it ('link' it). To fix the problem, we must define the function with the matching signature:

bar.cpp

#include "bar.h"
void bar(int x) { x++; } // now we've generated some code for the linker to 'link' the previously unresolved dependency to

Now the code compiles and links. So, we've covered half of your problem, the other half is that the linker was unable to resolve the external variables that you were trying to access. So, if you understand everything so far, it's just more of the same. An extern variable means that the compiler will allow usage of a variable without actually knowing anything about it (starting with a new set of code):

foo.h:

#pragma once
extern int foo;

foo.cpp:

#include "foo.h"
int foo;

bar.h:

#pragma once
#include "foo.h"

bar.cpp:

#include "bar.h"
void bar() {
foo = 5; // this can be resolved by the linker since we know about foo from foo.h through bar.h
}

We can see that foo is a global int that anything that includes foo.h is able to access. The linker resolves the variable when creating the final executable. Alpha_ProgDes gave the right way to fix your problem (since your source file would now know about the extern SDL_Surfaces, which would allow the function/variables to be used).

The reason your old code worked was lucky inclusion order. Depending on the order that things are compiled, you can cheat the system, but this is a dangerous way to go about things, as the build will become very fragile based on the order of inclusions (certain dependency graphs are also impossible using that method). Projects built like that may, in effect, have *one* source file (aka compilation unit, since #include literally inserts source into a file), so everything must be defined in the right order instead of the proper way where it doesn't matter, since you're using the compiler and linker correctly. One other advantage you'll find in a large project is that where you used to have to recompile the entirety of the source for any change you made (since it was in effect one compilation unit), now your project can compile *only* the source that has changed, which is really great (can you tell I learned all this the hard way?).

This also explains libraries. Libraries are just compiled code. The header files for a library are just there to let source code depend on the library, and the linker will find the code and resolve the dependencies. For dynamic libraries, the 'cannot resolve dependency' error will come at run-time (eg when you're missing a dll), but it's the same principle.

I've sadly left out quite a bit, and used C for brevity, but C++ works the same way. In short:

• functions/methods (code units with a particular signature), and variables with any particular name can only be *defined* once in a compilation unit. A compilation unit is the entirety of the code being compiled (ie after all the #included files are inserted to make one big 'unit')
• to get code to compile, there *must* be *either* a definition or a declaration within the compilation unit.
• any variable/function that is only declared (not defined) within a compilation unit *must* be resolved by the linker if it is used (ie you can declare something and never define it if you never try to use it anywhere).
• a declaration *must* be resolved for an executable to be generated. If the definition is not within the compilation unit, it is the linker's job to resolve the dependency.

Edited by giant city games, 24 March 2013 - 09:53 PM.

#11SuperVGA  Members   -  Reputation: 1132

Like
0Likes
Like

Posted 25 March 2013 - 07:51 AM

Like I said I was not up on my CPP and also asked for help in clarification,  So I guess by trying and asking fo help you get knocked down.

This is why people do not try.

If you know CPR, or if you're a paramedic, it's cool to help a person who collapsed on the street.

I would think twice before electrocuting people, or going "scalpel!" when I don't really know what I'm doing.

Even if I just want to help, try at home first - it won't mess up other peoples' interpretation of "correct".

[I] have been out of CPP for awhile and I am trying to get back into it.  I realize newer versions of CPP have changed and so has the format.

That part never changed, though.

Edited by SuperVGA, 25 March 2013 - 07:52 AM.

#12Serapth  Crossbones+   -  Reputation: 6580

Like
0Likes
Like

Posted 25 March 2013 - 10:04 AM

A part of the confusion of .h(pp) files comes from the preprocessor, this is an important concept to grok for C++, but it isn't intuitive, especially if you come from a language where they don't exist or are much less prevalent.

Essentially the pre-processor is a process that runs on your program *BEFORE* it is compiled.

When the preprocessor runs, it scans through your code looking for pre-processor directives.  Those are the lines that start with the pound (#) symbol, such as #define #pragma or #ifdef.

Most of the time, the preprocessor is simply generating the code file that will be sent to the compiler.  Consider the following common scenario with the preprocessor:

#ifdef _WIN32

int i = 42;

#else

int i = 43;

#endif

The preprocessor see this directives, and if you are running on Windows the final result is:

int i = 42;

If you are running on any other platform, the result becomes:

int i = 43;

Similarly, thats how header guards work, when you encounter:

#ifndef _BOB_H

#define _BOB_H

#endif

The preprocessor starts processing this file, checks if _BOB_H has been defined, if it hasn't it continues as usual, defining the value _BOB_H.  Therefore, the next time this .h file is #included, it fails the test ( since _BOB_H was defined on the prior pass ) and skips the file.  #pragma once is a shorthand (slightly un-standard, but standard enough to use it freely ) for the exact same thing.

Finally back to #include "bob.h".

When the preprocessor runs through your code and see's the line

#include "bob.h"

It simply is opening that file and copying the contents into where it was included.

#13Poigahn  Crossbones+   -  Reputation: 564

Like
1Likes
Like

Posted 25 March 2013 - 01:08 PM

TTFN

Edited by Poigahn, 25 March 2013 - 02:54 PM.

Your Brain contains the Best Program Ever Written : Manage Your Data Wisely !!

#14Serapth  Crossbones+   -  Reputation: 6580

Like
4Likes
Like

Posted 25 March 2013 - 01:13 PM

You are mixing C++ up with Managed C++, C++/CLI, C++. NET or whatever the he'll Microaoft calls it these days.

cout is unchanged.

#15Cornstalks  Crossbones+   -  Reputation: 7020

Like
4Likes
Like

Posted 25 March 2013 - 01:28 PM

I don't think people will gang up on you and give you a million -1 votes,

You want to re-think this one ?

Yeah, sorry, I was wrong. I think if you had edited your post and added the strikethrough I suggested, a large number of downvotes could have been mitigated. But I frequent these forums a lot more than I should, and maybe you don't frequent them as much, so you didn't see my suggestion until today. Seeing as this is the "For Beginners" forum, technical accuracy is imperitive, and I'm guessing that's one reason why a large number of downvotes have been given. To be honest, I think you've received too many downvotes (yes, it's incorrect technical information, but I think your attitude has been rather positive and you did preface it with a warning).

I would like to state that I was not entirely incorrect on my original post.  I was wrong yes.  And what happened was, since I have been out of it for sometime, had several languages crossed.
IN VB6, you have to declare a class as PUBLIC in order to share it between modules.  So I had my wires crossed there!
In PASCAL you have to predeclare shared functions/procedures prior to actually using each.  SO I had my wires crossed there.

Yeah, I guessed you might have been mixing languages.

C++ has been like this for decades... Granted, C++ wasn't standardized as a language until 1998, so if you learned C++ before 1998, it's possible you learned someone's custom flavor of C++.

Now I have a quetion,  If C++  Has Not Changed since 1998, then why is some code not recompiling in Visual Studio 2010 ?  A consol app written in Studio 6 or 2008 will not recompile in studio 2010.

Heh, you brought up Visual Studio 6. Visual Studio 6 was released in June 1998. C++ was first (officially) standardized in September (if I'm not mistaken) 1998. Which is one reason why Visual Studio 6 has several discrepancies from standard C++. Additionally, with each compiler release, bugs are fixed and "compiler extensions" are changed/added/removed (so a new compiler with better standards compliance might break old non-compliant code). And on top of all of this, I never said "C++ has not changed (ever)." I said "C++ has been like this" (where "this" is specifically referring to the whole public: syntax and meaning). C++ has changed several times. There was a new standard released in 2003, and again a new standard in 2011 (2003 introduced some minor changes, 2011 introduced some major changes). C++ has certainly changed over the years, but it has not changed in regards to the things we've discussed in our context.

Correct me,  And I am certain to hammered on this, but cout << has been replaced by Console Writeline  for console applications.

And correct you I shall. Console.WriteLine is Microsoft .NET stuff. std::cout << is C++. std::cout has not been replaced in any way by Console.WriteLine. They're entirely different things (Microsoft .NET is Microsoft's own framework and has nothing to do with standard C++). You've mixed things up again.

I'm not going to derail this thread anymore. I don't want chiranjivi's topic to go off track. If you want to discuss this more, I suggest starting a new thread (or PMing people if there's anyone in specific you want to talk to).

Edited by Cornstalks, 25 March 2013 - 01:32 PM.

[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#16chiranjivi  Members   -  Reputation: 208

Like
0Likes
Like

Posted 27 March 2013 - 12:46 AM

Hi all,

Many thanks for the posts, Cornstalks and giant city games especially for your detailed and informative breakdowns of what was going on. After reading your responses and googling a little more I think I have a much better grasp of things. The problem was defining functions/variables in the sdl_functions header and then trying to include the header at every point where they were required; not understanding what the header files did meant I didn't understand why this wasn't a good idea! I appreciate all the feedback and everyone's attempts to help.

#17Fs02  Members   -  Reputation: 477

Like
0Likes
Like

Posted 27 March 2013 - 09:20 AM

Hi there's, i think you can make a precompiled header like "stdafx.h" and include all sdl header there, then you can include this header to all of your program

some think like this :

stdafx.h

#ifndef STDAFX_H_INCLUDED
#define STDAFX_H_INCLUDED

//include all needed header after this line
#include "sdl_functions.h"

#endif // STDAFX_H_INCLUDED


level.h

#ifndef LEVEL_H_INCLUDED
#define LEVEL_H_INCLUDED

#include "stdafx.h"

//program class code/definition

#endif // LEVEL_H_INCLUDED


what you need to do is just include the precompiled liblary in all of your class definition file(.h)

hmm, i don't know is that would solve your problem but i just want to show you how i manage to include my external liblary and distribute it to all of my code

good luck

Edited by Fs02, 27 March 2013 - 09:20 AM.

[my game in #ggj14 : http://www.indiedb.com/games/romance-stalker [my blog : http://surya.volge.net ]

#18Serapth  Crossbones+   -  Reputation: 6580

Like
0Likes
Like

Posted 27 March 2013 - 10:40 AM

Hi there's, i think you can make a precompiled header like "stdafx.h" and include all sdl header there, then you can include this header to all of your program

some think like this :

stdafx.h

#ifndef STDAFX_H_INCLUDED
#define STDAFX_H_INCLUDED

//include all needed header after this line
#include "sdl_functions.h"

#endif // STDAFX_H_INCLUDED


level.h

#ifndef LEVEL_H_INCLUDED
#define LEVEL_H_INCLUDED

#include "stdafx.h"

//program class code/definition

#endif // LEVEL_H_INCLUDED


what you need to do is just include the precompiled liblary in all of your class definition file(.h)

hmm, i don't know is that would solve your problem but i just want to show you how i manage to include my external liblary and distribute it to all of my code

good luck

While stdafx.h is what it used for precompiled headers, and what you are suggesting is perfectly valid, you are mixing up your expressions a little bit.

A precompiled header is actually a compiler optimization, you need to enable it.  The idea is to include all the header files that don't change often and have the compiler compile them all once in advance.  This is where the "pre-compiled" part comes in.  That means the next time you compile your code, these header files wont have to be compiled, speeding the process up.

#19Fs02  Members   -  Reputation: 477

Like
0Likes
Like

Posted 27 March 2013 - 10:52 AM

While stdafx.h is what it used for precompiled headers, and what you are suggesting is perfectly valid, you are mixing up your expressions a little bit.

A precompiled header is actually a compiler optimization, you need to enable it.  The idea is to include all the header files that don't change often and have the compiler compile them all once in advance.  This is where the "pre-compiled" part comes in.  That means the next time you compile your code, these header files wont have to be compiled, speeding the process up.

Yupp, that's what i mean, thanks for making it clear

[my game in #ggj14 : http://www.indiedb.com/games/romance-stalker [my blog : http://surya.volge.net ]

#20wintertime  Crossbones+   -  Reputation: 3771

Like
0Likes
Like

Posted 27 March 2013 - 12:19 PM

I dont think its a good idea to tell someone who barely understands what a header is to stuff everything into one header to avoid learning how headers are supposed to be working. That some people who know already how headers work use precompiled headers to speed up compilation is some completely unrelated subject.

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

PARTNERS