Code Shouldn't Work - But Why?

So I'm still learning C++, this was a bit of code that shouldn't and doesn't work. However, I don't fully understand why. Here are my questions - How does Main.cpp see the definition of getSquarePerimeter that's inside Square.cpp if it was never #include into Main.cpp? The second question is how come this code wont work - it looks like it should to me. The solution to making the code work is to remove the definition from Square.h into Square.cpp and have getSquareSides simply be a forward declaration - but what does that change to make it work? Lastly, even if you don't include #include Square.h in Square.cpp, the compiler will still assume that that Square.cpp is defining the already declared getSquareSides and getSquarePerimeter - how? How does the compiler assume that the definitions for them are in Square.cpp if Square.h was never #Include into Square.cpp?

Sorry for the ton of questions - But I'd rather not just assume things that would allow all this to make sense only for those assumptions to have been wrong. Thanks!

Square.h :

#ifndef SQUARE_H
#define SQUARE_H

int getSquareSides()
{
return 4;
}

int getSquarePerimeter(int sideLength); // forward declaration for getSquarePerimeter

#endif

-------------------------------------------------------------------------------------------------------
Square.cpp :

#include <iostream>
#include "stdafx.h"
#include "square.h" // square.h is included once here

int getSquarePerimeter(int sideLength)
{
return sideLength * getSquareSides();
}

-------------------------------------------------------------------------------------------------------
Main.cpp :

#include "stdafx.h"
#include <iostream>
#include "square.h" // square.h is also included once here

int main()
{
std::cout << "a square has " << getSquareSides() << " sides" << std::endl;
std::cout << "a square of length 5 has perimeter length " << getSquarePerimeter(5) << std::endl;

return 0;
}
Last edited on
How does Main.cpp see the definition of getSquarePerimeter that's inside Square.cpp if it was never #include into Main.cpp?
Building a program involves two steps: compilation and linking. First all files are compiled and then the results of those compilations are linked together.

If the compiler sees a definition, it will compile it and output it to the object file, but it if sees a declaration and a usage (calling or getting the address of the function) of that declaration it will instead add an item to the external references in the object file saying "I need function foo".

When the linker goes to link everything together, it should be able to see all definitions. It will go through the external references list of every object file and for every external reference it will scan the file looking for that reference and insert the correct address. If it doesn't know the address then that means a function was declared but not defined, or that a library file was not linked it; then it will generate a linker error complaining about an undefined or unresolved reference.

The second question is how come this code wont work - it looks like it should to me. The solution to making the code work is to remove the definition from Square.h into Square.cpp and have getSquareSides simply be a forward declaration - but what does that change to make it work?
When you #include a header in a source file, you're doing the equivalent as copy-pasting the text of the header into the source. If you do that in this case, every source file will have a definition for getSquareSides(). The compiler will accept this just file, because it can't see other source files being compiled, and will compile the function and output the generated code to the object file. But the linker will be able to see that you made defined the same function multiple times. A function must be defined exactly once. The linker is not able to resolve this situation, so it just generates a linker error about multiple defintions.

By the way, another solution would be to make the function static inline. Then the linker will accept multiple definitions and will deal with it accordingly.

Lastly, even if you don't include #include Square.h in Square.cpp, the compiler will still assume that that Square.cpp is defining the already declared getSquareSides and getSquarePerimeter - how? How does the compiler assume that the definitions for them are in Square.cpp if Square.h was never #Include into Square.cpp?
Defining a function also implicitly declares it. Declaration serves two purposes. First, to state the presence of functions accross separate translation units. Second, to state the presence of functions within the same translation unit when the order of declaration and usage do not match. As an example for this second situation, consider this:
1
2
3
4
5
6
7
8
9
10
//Uncomment to allow this to compile:
//void bar();

void foo(){
    bar();
}

void bar(){
    foo();
}
If you don't declare bar() before foo() is defined, the compiler won't know that the function exists anywhere.

If neither case is applicable, then there's no need to declare the function in addition to defining it.
Last edited on
Thanks a lot for the detailed answers, I really appreciate it. So, to be sure I understood correctly -

1. The reason it finds the definition even though its location isn't #include is because the linker will search through (all?) files connected with the project - and that #include is mostly only for the compiler to understand what you've written and know that you haven't just written gibberish.

2. The code wont work because Square.h has been #include into Square.cpp as well as Main.cpp . - This means that even though it was used like a header file, seeing getSquareSides being defined again in Square.cpp made it not run. This leads me to a small question. The program will treat .h and .cpp files differently in this case, correct? Since if I defined the same thing several times throughout many .h files, it would run correctly as long as I only #include one of the the header files only once. However, if a .cpp AND a .h file had the same definition of a variable - it wont execute. My thought is that the compiler wont look at the .h files you didn't #include and that the linker doesn't even look at .h files all together - Correct? Since if the linker looked at ALL files, it would look at the .h files and see multiple definitions, right?

3. I see, it's like my first question since the linker will look for definitions everywhere, it doesn't matter if the location of the definition has a #include for the declaration - In fact, the linker wont even see the #include if included, right?

Thanks, Helios - Your answers were well detailed and helpful !
Last edited on
1. Yes. If the compiler doesn't know the type of the identifier you're using somewhere it can't even parse the code.

2.
The code wont work because Square.h has been #include into Square.cpp as well as Main.cpp
No. The code won't link because there's a non-staticinline function definition in a header file that's included from multiple translation units.
The program will treat .h and .cpp files differently in this case, correct?
This question is a bit tricky. The answer depends on how you're invoking your compiler. Most compilers by default will ignore inputs with common header extensions and only compile inputs with common source extensions.
Strictly speaking, though, the compiler doesn't care about the extension of the file you're passing to it. All it sees is that it's been given a text file to compile. There's no real reason why you couldn't write your code in main.h and include some_header.cpp. Including .cpp or .inl is actually fairly common, in certain situations.
Since if I defined the same thing several times throughout many .h files, it would run correctly as long as I only #include one of the the header files only once.
Well, yes, the compiler will accept that kind of usage, but generally you should avoid defining functions in headers.
My thought is that the compiler wont look at the .h files you didn't #include and that the linker doesn't even look at .h files all together - Correct?
The linker ignores both source and header files. It only looks at object files, generated by the compiler.

3. Correct. Actually, even the compiler proper doesn't see #include directives. Those are resolved by the preprocessor. There's no difference between these two scenarios:
1
2
3
4
5
6
7
8
9
10
11
12
//A.h
void A();

//A.cpp
void A(){}

//B.cpp
#include "A.h"

void B(){
    A();
}

1
2
3
4
5
6
7
8
9
//A.cpp
void A(){}

//B.cpp
void A();

void B(){
    A();
}
Last edited on
2. I think that's what I meant to say if I understood correctly. Square.h had a definition in it. The code didn't work since Square.h was in both Square.cpp and Main.cpp - Therefore defining it twice in the two .cpp files. Right?

Thanks again, your insight is greatly appreciated and helpful!
Last edited on
You can define functions in headers and include those headers from multiple sources. You just can't define non-staticinline functions in headers that will be included from multiple sources.
Last edited on
I haven't yet gotten to learn static functions. By what you're saying, they allow you to have multiple definitions?
Oops. What a brain fart. Inline functions, not static functions. Inline functions can be defined in multiple translation units.
It's fine, lol. I guess these are things I'll get to in time. Thanks for all the help Helios!
> I haven't yet gotten to learn static functions. By what you're saying, they allow you to have multiple definitions?

Yes. Functions which are declared static have internal linkage; they are local to the current translation unit. We can have one definition of such a function per translation unit; in addition, the definitions in different translation units need not be the same.

For an inline function with external linkage, there may be more than one definition in the program as long as each definition appears in a different translation unit; however, all these different definitions must be identical.
Last edited on
Of note, multiply defining a static function may (will?) generate duplicate code, bloating the executable.
Of note, multiply defining the same static function in each translation unit may reduce the size of the executable.
Reduce compared to what?

EDIT: By the way, if you could keep the snark to a minimum, that's be great.
Last edited on
I see, thanks for information guys - its been very helpful and educational ! Hope you two are getting along !
Topic archived. No new replies allowed.