In C++ the data that defines an instance of an object is organized as a struct. The name of the object is essentially the name of a struct variable. The class member functions have direct access to the struct's fields, even if these fields are private data members. This direct access is implemented by passing the address of the object (the struct variable) as an implicit argument to the member function. In our environment, it is passed as the first (the left-most) argument, but it does not appear in the argument list.
Let us look at the simple fraction class used in the program in Listing 15.6.1 as an example. The programs in this section assume the existence of the functions:
writeStr—Displays a text string on the screen.
getDecInt—Reads a signed integer from the keyboard and returns it.
putDecInt—Displays a signed integer on the screen.
The fraction class is declared in the fraction.h header file, so we need to include it:
#include "fraction.h"
The fraction class contains both variable and function members. By default, members are private. Declaring the function members public allows them to be used by functions other than those within the class.
We make use of assembly language functions written earlier in the book, which are callable from C. When doing this, we need to tell the C++ compiler that these are C functions by using a prototype statement like:
extern "C" int newLine(void);
A class declaration defines a new type that the programmer can use. As with the built-in data types, we can declare a variable of type fraction:
fraction x;
In object-oriented programming, this is typically called “instantiating an object.” We can now send messages to this object. For example, the get message:
x.get();
will get values for the numerator and denominator in the x fraction from the user. This is also known as “invoking a method” or (especially in C++) “calling a member function.”
The assembly language generated by the g++ compiler Listing 15.6.4) shows the rationale for “calling a member function”.
The x.get() message passing is implemented in assembly language with a call to the _ZN8fraction3getEv function, passing the address of the x object to the function:
This may look like it is calling another function, not the get function. The C++ compiler uses an algorithm that combines the class name with the member function name, plus the data types of any arguments to the member function. This is called name mangling. There is no standard for how this is actually done, so each compiler may do it differently.
This is not a book about C++ programming, so we will move on now. The point that is made here is that there is nothing magical about object-oriented programming. It is merely a matter of the compiler translating (perhaps even mangling) the syntax into the appropriate assembly (ultimately, machine) language.
We now consider a way to implement this same program in C, Listing 15.6.6. We need to create our own “name mangling” here to ensure that each function has a unique name within this program.
Notice the use of the this pointer in the C equivalents of the “member” functions. Its place in the parameter list coincides with the implicit argument to C++ member functions—that is, the address of the object. The this pointer is implicitly available for use within C++ member functions. Its use depends upon the specific algorithm. You should now have a good understanding of how C++ implements objects.