4

I need help translating a concept from Java to C++.

In Java, when you create a class and give it methods (functions), you must define the function in that class so that it can be properly called by any class instance that might want to. For example, in the class Employee you'd declare and define the method salaryRaise(int amount). Whenever an Employee object wants to use it, it calls Employee.salaryRaise(i) and Java knows exactly where to find it - in the Employee class.

In C++, functions are declared in .h files and then defined somewhere else. How does the compiler know where to find this method?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
CodyBugstein
  • 21,984
  • 61
  • 207
  • 363
  • Without sample code, your question makes no sense at all. The function is declared in the .h, is written in the .cpp file, the variable that calls it has all the relevant types, and the compiler resolves all the references. Whats the problem? – abelenky Oct 23 '13 at 20:03
  • This question, (and other very elementary questions) will be answered in just about any beginner C++ book. See [here](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) and [here](http://stackoverflow.com/questions/194812/list-of-freely-available-programming-books). – JBentley Oct 23 '13 at 20:11
  • The same way as Java. The linker resolves it. Just because it knows it is in a specific class that does not mean you know where in memory the function lives. So at runtime you have to convert the name to a memory location. This is done by the runtime linker. Note: In Java the class loader does this job at the conceptual level. – Martin York Oct 23 '13 at 21:59
  • http://www.javaworld.com/jw-06-1997/jw-06-hood.html – Martin York Oct 23 '13 at 22:05

7 Answers7

6

That is really the job of the linker, not the compiler. The compiler will call the function referencing a symbol that is yet undefined. The linker will then get the different translation units and resolve the symbols by name (that is, the mangled names that encodes additional information as the namespaces, types of the arguments...)

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
4

You declare the function in the header (.h) file. That header file then gets #includeed in any other files where you need to use the function. This satisfies the compiler, because it knows the signature of the function and how to call it.

The function is then defined in the source (.cpp) file. The source file includes it's own header file, so that when you compile it, you end up with an object file containing the complete compiled code for that function.

The linker then links any parts of your code where you've called the function (i.e. the files where you included the header), with the actual code for that function.

EDIT with example:

Foo.h:

#ifndef FOO_H
#define FOO_H

class Foo
{
public:
   int fooFunction(double a);
};

#endif

Foo.cpp:

#include "Foo.h"

int Foo::fooFunction(double a)
{
   // Function definition
   return 1;
}

Compiling Foo.cpp generates Foo.obj which contains the complete definition for fooFunction. So far so good.

Bar.h:

#ifndef BAR_H
#define BAR_H

#include "Foo.h"

class Bar
{
public:
   void barFunction();
};

#endif

Bar.cpp:

#include "Bar.h"

void Bar::barFunction()
{
   Foo foo;
   int returnValue = foo.fooFunction(2.0);
}

Bar.cpp includes Bar.h which in turn includes Foo.h. When Bar.cpp gets preprocessed therefore, the declarations for Foo::fooFunction are inserted at the top of the file. So, when the statement int returnValue = foo.fooFunction(2.0); is compiled, the compiler knows how to emit the machine instructions to call fooFunction because it knows the type of the return value (int) and it knows the types of the parameters (a double, and an implicit this pointer for the foo object). Because no function definition was provided, the function will not be inlined (inlining means the entire code for the function is copied into the point at which it is called). Instead, a pointer to the memory address of the function is used to call it. Because a pointer is being used, the compiler doesn't care about the definition - all it needs to know is "I need to call a function X at memory location Y with parameters A and B, and I need to have an int sized memory section ready to store the return value, and I'll assume that the code at that address knows how to perform the function". However, the compiler has no way to know what the address of that function will be in advance (because the function definition lives in a separate .cpp file and will be part of a separate compilation job AKA translation unit).

That's where the linker comes in. Once all the translation units have been compiled (which could be in any arbitrary order), the linker goes back to the compiled code for Bar.cpp and links the two together by filling in the address for the now compiled definition of fooFunction at the point at which it is called in Bar.cpp, thus making the compiled code fully executable.

JBentley
  • 6,099
  • 5
  • 37
  • 72
  • Here's where I get confused; ok so you satisfy the compiler by declaring "behold a function named `foo(int i)`",in a `.h` file. Now you can call that function from anywhere, from ClassA, from ClassB etc. But what does the function mean - where is it defined?? If it's defined in ClassA, what happens if you call it from ClassB? ClassB can only check the `.h`, but the `.h` doesn't have any reference to the definition in ClassA. This is where I get lost. Something is missing from your explanation. Can you clarify? – CodyBugstein Oct 24 '13 at 09:00
  • @imray Re-edited to fix a couple of typos and make a couple of points clearer (hopefully). – JBentley Oct 24 '13 at 12:43
  • Note that inlining (and basically all kinds of other optimizations) can happen at any point (even by the linker or during execution time), it is really up to the compiler/linker/runtime package. But in general you are correct in your outline. – Attila Oct 24 '13 at 14:56
3

When the compiler transforms your source to binary, it usually builds intermediary files (aka object files) that contain (among other things) the member functions of a class.

At the time of linking the intermediary files are translated into machine code. If all member functions used can be resolved at that time, all is good, otherwise you get a linking error.

This way, you can place the definition of member functions anywhere in the project -- as long as the linker finds what it needs, it can build the binary. It is not advised, however as this approach will make reading/understanding the class purpose/mechanic harder for humans

Attila
  • 28,265
  • 3
  • 46
  • 55
  • Thanks for saying that explicity; that the method definitions can be scattered everywhere. So essentially, I can put the definition for the method `raiseSalary()` of the `Employee` class, inside the class `MaintenanceSupplies`? Wow I can see this going bad – CodyBugstein Oct 24 '13 at 09:08
  • 1
    @Imray - Indeed. And that is why Java (which was designed after C++) requires you to put all code relating to the class into one file. – Attila Oct 24 '13 at 14:58
3

In C++, functions are declared in .h files and then defined somewhere else. How does the compiler know where to find this method?

Because you tell it where to find it when you provide the definition of the method.

Consider a hypothetical header file:

class Gizmo
{
public:
  bool Foo();
};

Now we will define the Foo method above in another CPP file:

bool Gizmo::Foo()
{
  return true;
}

Here's the paraphrased dialogue you have with the above definition:

Ok, compiler. I'm defining a function which returns a bool. The function is part of the class Gizmo, and the function is named Foo. The function takes no parameters. The definition of the function is: return true.

The scope resolution token :: separates a name of an enclosing class and the name of the function.

If you had left that Gizmo:: bit out, and instead wrote:

bool Foo()
{
  return true;
}

The you would still be defining a function named Foo which takes no parameters and returns a bool, but now instead of defining it to be part of Gizmo, you are defining it to be on it's own. A so-called "free function" or "global function." This would compile just fine, but you would get an "unresolved external" linker error if some other code somewhere was trying to use Gizmo::Foo().

Attila
  • 28,265
  • 3
  • 46
  • 55
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Ok thanks for the answer but I still don't totally understand. If class `DooHickey` calls the method `Gizmo::Foo()` which is defined in some obscure class `Antique`, how does the program find the definition ?? There are no pointers to it from anywhere! Does C++ have to search everywhere each time a method is called to find a definition? – CodyBugstein Oct 24 '13 at 09:16
2

A compiler will typically have a compilation stage and a linking stage. The compilation stage compiles source files into object files. The linking stage will collect the object files and create an executable image. During the linking stage, symbols that were left unresolved in the compilation stage get resolved. This is not much different for either Java and C++. For example, it is how a Java class can call methods on a different Java class.

jxh
  • 69,070
  • 8
  • 110
  • 193
1

In C++ you determine the body by class name followed by :: and then method declaration:

class Employee
{
   void salaryRaise(int amount); // Now, compiler knows this method belongs to 
                                 // the class Employee
};

Then you can define the body:

void Employee::salaryRaise(int amount) // Now, compiler knows everything about
{    ^^^^^^^^^^                        // definition of the method
}

For generating object files (and executable binary), you have to pass the .cpp files to the compiler(actually the linker). Therefore everything is visible to the compiler.

masoud
  • 55,379
  • 16
  • 141
  • 208
  • But the body could be in any `.cpp` file, can't it? – CodyBugstein Oct 23 '13 at 20:04
  • Yes, and in linkage phase you should pass the related .cpp file to compiler and compiler uses it. – masoud Oct 23 '13 at 20:07
  • So the definition for the various methods in a class can be scattered across unlimited numbers of files? How does that make any sense? Especially coming from Java where you know you can find all methods of a class in the class file. – CodyBugstein Oct 24 '13 at 09:02
  • Yes it can. In C++ you have the power to control anything, you can organize classes and their implementation like Java or whatever you want. In C++ we know the definition is usually in the same _.h_ file or in the corresponding _.cpp_ file. – masoud Oct 24 '13 at 09:41
0

In your header file, you prototype/forward declare the class like this.

class Foo
{
private:
    int m_Fooint;
    int m_Fooint2;
public:
    Foo(int a, int b);
    int getFooTotal();
};

Then you would define the member functions like this

Foo::Foo(int a, int b) // prototypes names don't have to be the same
{
    Foo::m_Fooint = a;
    Foo::m_Fooint2 = b;
}

int Foo::getFooTotal()
{
    return Foo::m_Fooint + Foo::m_Fooint2;
}

Besides the constructor and destructor, you need to have the data type. Even if it's void and not returning anything.

So you might have something such as

float Foo::getSomeFloat();

or

double Foo::getSomeDouble();

or you can even return an object

OtherClass Foo::getOtherClassObject();
Austin J.
  • 138
  • 6