Chapter 2: Introduction

Don't hesitate to send in feedback: send an e-mail if you like the C++ Annotations; if you think that important material was omitted; if you find errors or typos in the text or the code examples; or if you just feel like e-mailing. Send your e-mail to Frank B. Brokken.

Please state the document version you're referring to, as found in the title (in this document: 6.2.3) and please state chapter and paragraph name or number you're referring to.

All received mail is processed conscientiously, and received suggestions for improvements will usually have been processed by the time a new version of the Annotations is released. Except for the incidental case I will normally not acknowledge the receipt of suggestions for improvements. Please don't interpret this as me not appreciating your efforts.

This document offers an introduction to the C++ programming language. It is a guide for C/C++ programming courses, yearly presented by Frank at the University of Groningen. This document is not a complete C/C++ handbook, as much of the C-background of C++ is not covered. Other sources should be referred to for that (e.g., the Dutch book De programmeertaal C, Brokken and Kubat, University of Groningen, 1996).

The reader should realize that extensive knowledge of the C programming language is actually assumed. The C++ Annotations continue where topics of the C programming language end, such as pointers basic flow control and the construction of functions.

The version number of the C++ Annotations (currently 6.2.3) is updated when the contents of the document change. The first number is the major number, and will probably not be changed for some time: it indicates a major rewriting. The middle number is increased when new information is added to the document. The last number only indicates small changes; it is increased when, e.g., series of typos are corrected.

This document is published by the Computing Center, University of Groningen, the Netherlands. This document was typeset using the yodl formatting system.

All rights reserved. No part of this document may be published or changed without prior consent of the author. Direct all correspondence concerning suggestions, additions, improvements or changes to this document to the author:

Frank B. Brokken
Computing Center, University of Groningen
Nettelbosje 1,
P.O. Box 11044,
9700 CA Groningen
The Netherlands
(email: f.b.brokken@rc.rug.nl)

In this chapter a first impression of C++ is presented. A few extensions to C are reviewed and the concepts of object based and object oriented programming (OOP) are briefly introduced.

2.1: What's new in the C++ Annotations

This section is modified when the first or second part of the version number changes (and sometimes for the third part as well)

2.2: C++'s history

The first implementation of C++ was developed in the nineteen-eighties at the AT&T Bell Labs, where the Unix operating system was created.

C++ was originally a `pre-compiler', similar to the preprocessor of C, which converted special constructions in its source code to plain C. This code was then compiled by a normal C compiler. The `pre-code', which was read by the C++ pre-compiler, was usually located in a file with the extension .cc, .C or .cpp. This file would then be converted to a C source file with the extension .c, which was compiled and linked.

The nomenclature of C++ source files remains: the extensions .cc and .cpp are still used. However, the preliminary work of a C++ pre-compiler is in modern compilers usually included in the actual compilation process. Often compilers will determine the type of a source file by its extension. This holds true for Borland's and Microsoft's C++ compilers, which assume a C++ source for an extension .cpp. The Gnu compiler g++, which is available on many Unix platforms, assumes for C++ the extension .cc.

The fact that C++ used to be compiled into C code is also visible from the fact that C++ is a superset of C: C++ offers all possibilities of C, and more. This makes the transition from C to C++ quite easy. Programmers familiar with C may start `programming in C++' by using source files having extensions .cc or .cpp instead of .c, and may then comfortably slide into all the possibilities offered by C++. No abrupt change of habits is required.

2.2.1: History of the C++ Annotations

The original version of the C++ Annotations was written by Frank Brokken and Karel Kubat in Dutch using LaTeX. After some time, Karel rewrote the text and converted the guide to a more suitable format and (of course) to English in september 1994.

The first version of the guide appeared on the net in october 1994. By then it was converted to SGML.

Gradually new chapters were added, and the contents were modified and further improved (thanks to countless readers who sent us their comment).

The transition from major version three to major version four was realized by Frank: again new chapters were added, and the source-document was converted from SGML to yodl.

The C++ Annotations are not freely distributable. Be sure to read the legal notes.

Reading the annotations beyond this point implies that you are aware of the restrictions that we pose and that you agree with them.

If you like this document, tell your friends about it. Even better, let us know by sending email to Frank.

In the Internet, many useful hyperlinks exist to C++. Without even suggesting completeness (and without being checked regularly for existence: they might have died by the time you read this), the following might be worthwhile visiting:

2.2.2: Compiling a C program using a C++ compiler

For the sake of completeness, it must be mentioned here that C++ is `almost' a superset of C. There are some differences you might encounter when you simply rename a file to a file having the extension .cc and run it through a C++ compiler:

2.2.3: Compiling a C++ program

To compile a C++ program, a C++ compiler is needed. Considering the free nature of this document, it won't come as a surprise that a free compiler is suggested here. The Free Software Foundation ( FSF) provides at http://www.gnu.org a free C++ compiler which is, among other places, also part of the Debian (http://www.debian.org) distribution of Linux ( http://www.linux.org).

2.2.3.1: C++ under MS-Windows For MS-Windows Cygnus (http://sources.redhat.com/cygwin) provides the foundation for installing the Windows port of the Gnu g++ compiler.

When visiting the above URL to obtain a free g++ compiler, click on install now. This will download the file setup.exe, which can be run to install cygwin. The software to be installed can be downloaded by setup.exe from the internet. There are alternatives (e.g., using a CD-ROM), which are described on the Cygwin page. Installation proceeds interactively. The offered defaults are normally what you would want.

The most recent Gnu g++ compiler can be obtained from http://gcc.gnu.org. If the compiler that is made available in the Cygnus distribution lags behind the latest version, the sources of the latest version can be downloaded after which the compiler can be built using an already available compiler. The compiler's webpage (mentioned above) contains detailed instructions on how to proceed. In our experience building a new compiler within the Cygnus environment works flawlessly.

2.2.3.2: Compiling a C++ source text In general, the following command is used to compile a C++ source file `source.cc':

        g++ source.cc
This produces a binary program (a.out or a.exe). If the default name is not wanted, the name of the executable can be specified using the -o flag (here producing the program source):
        g++ -o source source.cc
If a mere compilation is required, the compiled module can be generated using the -c flag:
        g++ -c source.cc
This produces the file source.o, which can be linked to other modules later on.

Using the icmake program a maintenance script can be used to assist in the construction and maintenance of C++ programs. A generic icmake maintenance script is available, which has been tested on Linux platforms for several years now. Its description and components are found in a file named icmake-C1.61.tar.gz (or comparably), stored in the same location as the icmake program. Alternatively, the standard make program can be used for maintenance of C++ programs. It is strongly advised to start using maintenance scripts or programs early in the study of the C++ programming language.

2.3: C++: Advantages and claims

Often it is said that programming in C++ leads to `better' programs. Some of the claimed advantages of C++ are: Which of these allegations are true? Originally, our impression was that the C++ language was a little overrated; the same holding true for the entire object-oriented programming (OOP) approach. The enthusiasm for the C++ language resembling the once uttered allegations about Artificial-Intelligence (AI) languages like Lisp and Prolog: these languages were supposed to solve the most difficult AI-problems `almost without effort'. Obviously, too promising stories about any programming language must be overdone; in the end, each problem can be coded in any programming language (say BASIC or assembly language). The advantages or disadvantages of a given programming language aren't in `what you can do with them', but rather in `which tools the language offers to implement an efficient and understandable solution for a programming problem'.

Concerning the above allegations of C++, we support the following, however.

C++ in particular (and OOP in general) is of course not the solution to all programming problems. However, the language does offer various new and elegant facilities which are worthwhile investigating. At the same time, the level of grammatical complexity of C++ has increased significantly compared to C. This may be considered a serious disadvantage of the language. Although we got used to this increased level of complexity over time, the transition wasn't fast or painless. With the C++ Annotations we hope to help the reader to make the transition from C to C++ by providing, indeed, our annotations to what is found in some textbooks on C++. It is our hope that you like this document and may benefit from it: Enjoy and good luck on your journey into C++!

2.4: What is Object-Oriented Programming?

Object-oriented (and object-based) programming propagates a slightly different approach to programming problems than the strategy usually used in C programs. In C programming problems are usually solved using a ` procedural approach': a problem is decomposed into subproblems and this process is repeated until the subtasks can be coded. Thus a conglomerate of functions is created, communicating through arguments and variables, global or local (or static).

In contrast (or maybe better: in addition to) this, an object-based approach identifies keywords in a problem. These keywords are then depicted in a diagram and arrows are drawn between these keywords to define an internal hierarchy. The keywords will be the objects in the implementation and the hierarchy defines the relationship between these objects. The term object is used here to describe a limited, well-defined structure, containing all information about an entity: data types and functions to manipulate the data. As an example of an object oriented approach, an illustration follows:

The employees and owner of a car dealer and auto garage company are paid as follows. First, mechanics who work in the garage are paid a certain sum each month. Second, the owner of the company receives a fixed amount each month. Third, there are car salesmen who work in the showroom and receive their salary each month plus a bonus per sold car. Finally, the company employs second-hand car purchasers who travel around; these employees receive their monthly salary, a bonus per bought car, and a restitution of their travel expenses.
When representing the above salary administration, the keywords could be mechanics, owner, salesmen and purchasers. The properties of such units are: a monthly salary, sometimes a bonus per purchase or sale, and sometimes restitution of travel expenses. When analyzing the problem in this manner we arrive at the following representation: The hierarchy of the thus identified objects further illustrated in Figure 1.

Figure 1 is shown here.
Figure 1: Hierarchy of objects in the salary administration.


The overall process in the definition of a hierarchy such as the above starts with the description of the most simple type. Subsequently more complex types are derived, while each derivation adds a little functionality. From these derived types, more complex types can be derived ad infinitum, until a representation of the entire problem can be made.

In C++ each of the objects can be represented in a class, containing the necessary functionality to do useful things with the variables (called objects) of these classes. Not all of the functionality and not all of the properties of a class is usually available to objects of other classes. As we will see, classes tend to hide their properties in such a way that they are not directly modifiable by the outside world. Instead, dedicated functions are used to reach or modify the properties of objects. Also, these objects tend to be self-contained. They encapsulate all the functionality and data required to perform their tasks and to uphold the object's integrity.

2.5: Differences between C and C++

In this section some examples of C++ code are shown. Some differences between C and C++ are highlighted.

2.5.1: Namespaces

C++ introduces the notion of a namespace: all symbols are defined in a larger context, called a namespace. Namespaces are used to avoid name conflicts that could arise when a programmer would like to define a function like sin(), operating on degrees but does not want to lose the capability of using the standard sin() function, operating on radians.

Namespaces are covered extensively in section 3.7. For now it should be noted that most compilers require the explicit declaration of a standard namespace: std. So, unless otherwise indicated, it is stressed that all examples in the Annotations now implicitly use the

        using namespace std;
declaration. So, if you actually intend to compile the examples given in the Annotations, make sure that the sources start with the above using declaration.

2.5.2: End-of-line comment

According to the ANSI definition, ` end of line comment' is implemented in the syntax of C++. This comment starts with // and ends with the end-of-line marker. The standard C comment, delimited by /* and */ can still be used in C++:
    int main()
    {
        // this is end-of-line comment
        // one comment per line

        /*
            this is standard-C comment, covering
            multiple lines
        */
    }
Despite the example, it is advised not to use C type comment inside the body of C++ functions. At times you will temporarily want to suppress sections of existing code. In those cases it's very practical to be able to use standard C comment. If such suppressed code itself contains such comment, it would result in nested comment-lines, resulting to compiler errors. Therefore, the rule of thumb is not to use C type comment inside the body of C++ functions.

2.5.3: NULL-pointers vs. 0-pointers

In C++ all zero values are coded as 0. In C, where pointers are concerned, NULL is often used. This difference is purely stylistic, though one that is widely adopted. In C++ there's no need anymore to use NULL, and using 0 is actually preferred when indicating null-pointer values.

2.5.4: Strict type checking

C++ uses very strict type checking. A prototype must be known for each function before it is called, and the call must match the prototype. The program
    int main()
    {
        printf("Hello World\n");
    }
does often compile under C, though with a warning that printf() is not a known function. Many C++ compilers will fail to produce code in such a situation. The error is of course the missing #include <stdio.h> directive.

Although, while we're at it: in C++ the function main() always uses the int return value. It is possible to define int main(), without an explicit return statement, but a return statement without an expression cannot be given inside the main() function: a return statement in main() must always be given an int-expression. For example:

    int main()
    {
        return;     // won't compile: expects int expression
    }

2.5.5: A new syntax for casts

Traditionally, C offers the following cast construction:
        (typename)expression
in which typename is the name of a valid type, and expression an expression. Apart from the C style cast (now deprecated) C++ also supports the function call notation:
        typename(expression)
This function call notation is not actuallly a cast, but the request to the compiler to construct an (anonymous) variable of type typename from the expression expression. This form is actually very often used in C++, but should not be used for casting. Instead, four new-style casts were introduced:

2.5.5.1: The `static_cast'-operator The static_cast<type>(expression) operator is used to convert one type to an acceptable other type. E.g., double to int. An example of such a cast is, assuming d is of type double and a and b are t(int)-type variables. In that situation, computing the floating point quotient of a and b requires a cast:

        d = static_cast<double>(a) / b;
If the cast is omitted, the division operator will cut-off the remainder, as its operands are int expressions. Note that the division should be placed outside of the cast. If not, the (integer) division will be performed before the cast has a chance to convert the type of an operand to double. Another nice example of code in which it is a good idea to use the static_cast<>()-operator is in situations where the arithmetic assignment operators are used in mixed-type situations. E.g., consider the following expression (assume doubleVar is a variable of type double):
        intVar += doubleVar;
This statement actually evaluates to:
        intVar = static_cast<int>(static_cast<double>(intVar) + doubleVar);
IntVar is first promoted to a double, and is then added as double to doubleVar. Next, the sum is cast back to an int. These two conversions are a bit overdone. The same result is obtained by explicitly casting the doubleVar to an int, thus obtaining an int-value for the right-hand side of the expression:
        intVar += static_cast<int>(doubleVar);

2.5.5.2: The `const_cast'-operator The const_cast<type>(expression) operator is used to undo the const-ness of a (pointer) type. Assume that a function fun(char *s) is available, which performs some operation on its char *s parameter. Furthermore, assume that it's known that the function does not actually alter the string it receives as its argument. How can we use the function with a string like char const hello[] = "Hello world"?

Passing hello to fun() produces the warning

        passing `const char *' as argument 1 of `fun(char *)' discards const
which can be prevented using the call
        fun(const_cast<char *>(hello));

2.5.5.3: The `reinterpret_cast'-operator The reinterpret_cast<type>(expression) operator is used to reinterpret pointers. For example, using a reinterpret_cast<>() the individual bytes making up a double value can easily be reached. Assume doubleVar is a variable of type double, then the individual bytes can be reached using

        reinterpret_cast<char *>(&doubleVar)
This particular example also suggests the danger of the cast: it looks as though a standard C-string is produced, but there is not normally a trailing 0-byte. It's just a way to reach the individual bytes of the memory holding a double value.

More in general: using the cast-operators is a dangerous habit, as it suppresses the normal type-checking mechanism of the compiler. It is suggested to prevent casts if at all possible. If circumstances arise in which casts have to be used, document the reasons for their use well in your code, to make double sure that the cast will not eventually be the underlying cause for a program to misbehave.

2.5.5.4: The `dynamic_cast'-operator The dynamic_cast<>() operator is used in the context of polymorphism. Its discussion is postponed until section 14.5.1.

2.5.6: The `void' parameter list

Within C, a function prototype with an empty parameter list, such as
    void func();
means that the argument list of the declared function is not prototyped: the compiler will not warn against improper argument usage. In C, to declare a function having no arguments, the keyword void is used:
    void func(void);
As C++ enforces strict type checking, an empty parameter list indicates the absence of any parameter. The keyword void can thus be omitted: in C++ the above two function declarations are equivalent.

2.5.7: The `#define __cplusplus'

Each C++ compiler which conforms to the ANSI/ISO standard defines the symbol __cplusplus: it is as if each source file were prefixed with the preprocessor directive #define __cplusplus.

We shall see examples of the usage of this symbol in the following sections.

2.5.8: Using standard C functions

Normal C functions, e.g., which are compiled and collected in a run-time library, can also be used in C++ programs. Such functions, however, must be declared as C functions.

As an example, the following code fragment declares a function xmalloc() as a C function:

    extern "C" void *xmalloc(unsigned size);
This declaration is analogous to a declaration in C, except that the prototype is prefixed with extern "C".

A slightly different way to declare C functions is the following:

    extern "C"
    {
        // C-declarations go in here
    }
It is also possible to place preprocessor directives at the location of the declarations. E.g., a C header file myheader.h which declares C functions can be included in a C++ source file as follows:
    extern "C"
    {
        #include <myheader.h>
    }
Although these two approaches can be used, they are actually seldomly encountered in C++ sources. We will encounter a more frequently used method to declare external C functions in the next section.

2.5.9: Header files for both C and C++

The combination of the predefined symbol __cplusplus and of the possibility to define extern "C" functions offers the ability to create header files for both C and C++. Such a header file might, e.g., declare a group of functions which are to be used in both C and C++ programs.

The setup of such a header file is as follows:

    #ifdef __cplusplus
    extern "C"
    {
    #endif
        // declaration of C-data and functions are inserted here. E.g.,
        void *xmalloc(unsigned size);

    #ifdef __cplusplus
    }
    #endif
Using this setup, a normal C header file is enclosed by extern "C" { which occurs at the start of the file and by }, which occurs at the end of the file. The #ifdef directives test for the type of the compilation: C or C++. The `standard' C header files, such as stdio.h, are built in this manner and are therefore usable for both C and C++.

In addition to this, C++ headers should support include guards. In C++ is is usually undesirable to include the same header file twice in the same source file. Such multiple inclusions can easily be achieved by including an #ifndef directive in the header file. For example:

    #ifndef _MYHEADER_H_
    #define _MYHEADER_H_
        // declarations of the header file is inserted here,
        // using #ifdef __cplusplus etc. directives
    #endif
When this file is scanned for the first time by the preprocessor, the symbol _MYHEADER_H_ is not yet defined. The #ifndef condition succeeds and all declarations are scanned. In addition, the symbol _MYHEADER_H_ is defined.

When this file is scanned for a second time during the same compilation, the symbol _MYHEADER_H_ has been defined and consequently all information between the #ifndef and #endif directives is skipped by the compiler.

In this context the symbol name _MYHEADER_H_ serves only for recognition purposes. E.g., the name of the header file can be used for this purpose, in capitals, with an underscore character instead of a dot.

Apart from all this, the custom has evolved to give C header files the extension .h, and to give C++ header files no extension. For example, the standard iostreams cin, cout and cerr are available after including the preprocessor directive #include <iostream>, rather than #include <iostream.h> in a source. In the Annotations this convention is used with the standard C++ header files, but not everywhere else (Frankly, we tend not to follow this convention: our C++ header files still have the .h extension, and apparently nobody cares...).

There is more to be said about header files. In section 6.5 the preferred organization of C++ header files is discussed.

2.5.10: Defining local variables

In C local variables can only be defined at the top of a function or at the beginning of a nested block. In C++ local variables can be created at any position in the code, even between statements.

Furthermore, local variables can be defined inside some statements, just prior to their usage. A typical example is the for statement:

    #include <stdio.h>

    int main()
    {
        for (register int i = 0; i < 20; i++)
            printf("%d\n", i);
        return 0;
    }
In this code fragment the variable i is created inside the for statement. According to the ANSI-standard, the variable does not exist prior to the for-statement and not beyond the for-statement. With some older compilers, the variable continues to exist after the execution of the for-statement, but a warning like
warning: name lookup of `i' changed for new ANSI `for' scoping using obsolete binding at `i'
will then be issued when the variable is used outside of the for-loop. The implication seems clear: define a variable just before the for-statement if it's to be used after that statement, otherwise the variable can be defined inside the for-statement itself.

Defining local variables when they're needed requires a little getting used to. However, eventually it tends to produce more readable and often more efficient code than defining variables at the beginning of compound statements. We suggest the following rules of thumb for defining local variables:

If considered appropriate, nested blocks can be used to localize auxiliary variables. However, situations exist where local variables are considered appropriate inside nested statements. The just mentioned for statement is of course a case in point, but local variables can also be defined within the condition clauses of if-else statements, within selection clauses of switch statements and condition clauses of while statements. Variables thus defines will be available in the full statement, including its nested statements. For example, consider the following switch statement:

    #include <stdio.h>

    int main()
    {
        switch (char c = getchar())
        {
            case 'a':
            case 'e':
            case 'i':
            case 'o':
            case 'u':
                printf("Saw vowel %c\n", c);
            break;

            case EOF:
                printf("Saw EOF\n");
            break;

            default:
                printf("Saw other character, hex value 0x%2x\n", c);
        }
    }
Note the location of the definition of the character `c': it is defined in the expression part of the switch() statement. This implies that `c' is available only in the switch statement itself, including its nested (sub)statements, but not outside the scope of the switch.

The same approach can be used with if and while statements: a variable that is defined in the condition part of an if and while statement is available in their nested statements. However, one should realize that:

The latter point of attention should come as no big surprise: in order to be able to evaluate the logical condition of an if or while statement, the value of the variable must be interpretable as either zero (false) or non-zero (true). Usually this is no problem, but in C++ objects (like objects of the type std:string (cf. chapter 4)) are often returned by functions. Such objects may or may not be interpretable as numerical values. If not (as is the case with std::string objects), then such variables can not be defined in the condition or expression parts of condition- or repetition statements. The following example will, therefore, not compile:
    if (std::string myString = getString())     // assume getString() returns
    {                                           // a std::string value
        // process myString
    }

The above deserves further clarification. Often a variable can profitably be given local scope, but an extra check is required immediately following its initialization. Both the initialization and the test cannot be combined in one expression, but two nested statements are required. The following example will therefore not compile either:

    if ((char c = getchar()) && strchr("aeiou", c))
        printf("Saw a vowel\n");
If such a situation occurs, either use two nested if statements, or localize the definition of char c using a nested compound statement. Actually, other approaches are possible as well, like using exceptions (cf. chapter 8) and specialized functions, but that's jumping a bit too far ahead. At this point in our discussion, we can suggest one of the following approaches to remedy the problem introduced by the last example:
    if (char c = getchar())             // nested if-statements
        if (strchr("aeiou", c))
            printf("Saw a vowel\n");

    {                                   // nested compound statement
        char c = getchar();
        if (c && strchr("aeiou", c))
        printf("Saw a vowel\n");
    }

2.5.11: Function Overloading

In C++ it is possible to define functions having identical names but performing different actions. The functions must differ in their parameter lists (and/or in their const attribute). An example is given below:
    #include <stdio.h>

    void show(int val)
    {
        printf("Integer: %d\n", val);
    }

    void show(double val)
    {
        printf("Double: %lf\n", val);
    }

    void show(char *val)
    {
        printf("String: %s\n", val);
    }

    int main()
    {
        show(12);
        show(3.1415);
        show("Hello World\n!");
    }
In the above fragment three functions show() are defined, which only differ in their parameter lists: int, double and char *. The functions have identical names. The definition of several functions having identical names is called ` function overloading'.

It is interesting that the way in which the C++ compiler implements function overloading is quite simple. Although the functions share the same name in the source text (in this example show()), the compiler (and hence the linker) use quite different names. The conversion of a name in the source file to an internally used name is called ` name mangling'. E.g., the C++ compiler might convert the name void show (int) to the internal name VshowI, while an analogous function with a char* argument might be called VshowCP. The actual names which are internally used depend on the compiler and are not relevant for the programmer, except where these names show up in e.g., a listing of the contents of a library.

A few remarks concerning function overloading are:

2.5.12: Default function arguments

In C++ it is possible to provide ` default arguments' when defining a function. These arguments are supplied by the compiler when they are not specified by the programmer. For example:
    #include <stdio.h>

    void showstring(char *str = "Hello World!\n");

    int main()
    {
        showstring("Here's an explicit argument.\n");

        showstring();           // in fact this says:
                                // showstring("Hello World!\n");
    }
The possibility to omit arguments in situations where default arguments are defined is just a nice touch: the compiler will supply the missing argument unless explicitly specified in the call. The code of the program becomes by no means shorter or more efficient.

Functions may be defined with more than one default argument:

    void two_ints(int a = 1, int b = 4);

    int main()
    {
        two_ints();            // arguments:  1, 4
        two_ints(20);          // arguments: 20, 4
        two_ints(20, 5);       // arguments: 20, 5
    }
When the function two_ints() is called, the compiler supplies one or two arguments when necessary. A statement as two_ints(,6) is however not allowed: when arguments are omitted they must be on the right-hand side.

Default arguments must be known to the compiler when the code is generated where the arguments may have to be supplied. Therefore, the default arguments are usually mentioned in the function's declaration:

    // sample header file
    extern void two_ints(int a = 1, int b = 4);

    // code of function in, say, two.cc
    void two_ints(int a, int b)
    {
        ...
    }
Note that supplying the default arguments in function definitions instead of in the header file is not the correct approach: the compiler will read the header file and not the function definition when the function is used in other sources. Consequently, in those cases the compiler has no way to determine the values of default function arguments.

2.5.13: The keyword `typedef'

The keyword typedef is still allowed in C++, but is not required anymore when defining union, struct or enum definitions. This is illustrated in the following example:
    struct somestruct
    {
        int     a;
        double  d;
        char    string[80];
    };
When a struct, union or other compound type is defined, the tag of this type can be used as type name (this is somestruct in the above example):
    somestruct what;

    what.d = 3.1415;

2.5.14: Functions as part of a struct

In C++ it is allowed to define functions as part of a struct. Here we encounter the first concrete example of an object: as previously was described (see section 2.4), an object is a structure containing all involved code and data.

A definition of a struct point is given in the code fragment below. In this structure, two int data fields and one function draw() are declared.

    struct point            // definition of a screen
    {                       // dot:
        int x;              // coordinates
        int y;              // x/y
        void draw(void);    // drawing function
    };
A similar structure could be part of a painting program and could, e.g., represent a pixel in the drawing. With respect to this struct it should be noted that: The point structure could be used as follows:
    point a;                // two points on
    point b;                // the screen

    a.x = 0;                // define first dot
    a.y = 10;               // and draw it
    a.draw();

    b = a;                  // copy a to b
    b.y = 20;               // redefine y-coord
    b.draw();               // and draw it
The function that is part of the structure is selected in a similar manner in which data fields are selected; i.e., using the field selector operator (.). When pointers to structs are used, -> can be used.

The idea behind this syntactical construction is that several types may contain functions having identical names. E.g., a structure representing a circle might contain three int values: two values for the coordinates of the center of the circle and one value for the radius. Analogously to the point structure, a function draw() could be declared which would draw the circle.