Previous Table of Contents Next


7.2. C++ Fundamentals

As its name suggests, C++ is based on C. This lineage is most easily seen in the fundamental facilities. The overall structure of a program, the built-in types (aside from the bool type), and the operators all come from C. C programmers will therefore find much that is familiar in this section, and may therefore wish to skim it and dwell only on those details that do not strike a familiar chord.

7.2.1. Program Structure

A C++ program consists of one or more translation units. Loosely speaking, a translation unit is a part of a program that can be compiled separately from other translation units. More precisely, a translation unit is the result of applying a preliminary compilation phase called preprocessing to a source file.

A source file usually begins with one or more #include directives, each of which causes the preprocessor to copy declarations of entities (functions, global variables, types, and so on) that are defined in other translation units.

As an example, let us return to our sample program from section 7.1.2:

   #include <iostream>
   using namespace std;
   int main()

   {
       cout << “Hello, world!” << endl;
       return 0;
   }

Preprocessing this source file yields a translation unit that defines a single function named main. Every C++ program is required to define the main function in exactly one of its translation units; the C++ implementation executes the program by calling main as a subroutine.

We have already mentioned that the #include directive makes available an assortment of facilities that permit input/output. It can be useful to think of the way it does so as by copying appropriate declarations into the source text at the point of the #include. This way of viewing the behavior of #include is not fully accurate, however, because of the angle brackets (< >) surrounding the name iostream. These brackets are reserved for naming facilities that are built into the standard library, which means that the implementation is free to take advantage of knowledge of the nature of those facilities. So, for example, a compiler is permitted to recognize

   #include <iostream>

as a special case and “turn on” access to an assortment of precompiled code.

Nevertheless, the notion of treating #include as a form of textual substitution is useful—and indeed it is common to use that textual substitution facility when putting source programs together. As a simple example, suppose that we have a program that consists of many translation units, each of which begins with

   #include <iostream>
   using namespace std;

Then we might want to put those two lines into a file called, say, iolib, after which we could rewrite our sample program to look like this:

   #include “iolib”

   int main()
   {
       cout << “Hello, world!” << endl;
       return 0;
   }

Notice that the #include directive now uses double quotes instead of angle brackets. This usage indicates that the string inside the quotes should be translated to the name of a file in an implementation-defined way, and a copy of the contents of the file inserted in the program in place of the #include directive.

To make this example a little less simplistic, suppose we wanted to rewrite our program so that the code to print Hello, world! did not have to appear directly in main. Then we might write a translation unit that looks like this:

   // file greet.c

   #include <iostream>

   using namespace std;
   void greet()
   {
       cout << “Hello, world!” << endl;
   }

We have changed the name of our routine from main, which the system knows is special, to greet, which has no magical properties. We have also changed the return type from int to void, because we don’t really want the obligation to tell our caller anything about what we did.

At this point, the typical C++ implementation would let us compile the greet.c file, but we would not be able to execute it because there is no main function. To solve that problem, we will define a second translation unit:

   // file main.c

   void greet();

   int main()
   {
       greet();
       return 0;
   }

Our main.c file contains a declaration of greet, but the file doesn’t define greet. Because we did not define greet, in this file, the compiler assumes that greet is defined in a different translation unit.

We should now be able to compile main.c, but we still cannot execute it because there is no definition for greet. However, if we have already compiled greet.c, the typical C++ implementation will give us a way to combine the compiler output from greet.c and main.c and execute the resulting program.

There is one more important subtlety in constructing programs from source files: How can we tell that the declaration of greet (in main.c) and its definition (in greet.c) are consistent if the two files are compiled separately? The first step is to repeat the declaration in the file that contains the definition:

   // file greet.c, revised version
   #include <iostream>

   using namespace std;

   void greet();

   void greet()
   {
       cout << “Hello, world!” << endl;
   }

There is no harm in declaring and defining greet in the same translation unit, and there is one significant benefit: It lets the compiler check consistency between the declaration and the definition. However, it is still conceivable that the declaration of greet in main.c might differ from the one in greet.c.


Previous Table of Contents Next