C++ Notes

These notes are intended to give an introduction to the C++ programming language. They have been adapted and extended from several sources (see credits at the end of these notes). The material can be easily covered in a week for the reader with some experience in programming. At the end of that the reader will be able to write and run his own programs.
 
 

1. Introducing C++

1.1 The Origins of C++

C++ was developed by Bjarne Stroustrup of AT&T Bell Laboratories in the early 1980's, and is based on the C language. The "++" is a syntactic construct used in C (to increment a variable), and C++ is intended as an incremental improvement of C. Most of C is a subset of C++, so that most C programs can be compiled (i.e. converted into a series of low-level instructions that the computer can execute directly) using a C++ compiler.

C is in many ways hard to categorise. Compared to assembly language it is high-level, but it nevertheless includes many low-level facilities to directly manipulate the computer's memory. It is therefore an excellent language for writing efficient "systems" programs. But for other types of programs, C code can be hard to understand, and C programs can therefore be particularly prone to certain types of error. The extra object-oriented facilities in C++ are partly included to overcome these shortcomings.

1.2 ANSI C++

The American National Standards Institution (ANSI) provides "official" and generally accepted standard definitions of many programming languages, including C and C++. Such standards are important. A program written only in ANSI C++ is guaranteed to run on any computer whose supporting software conforms to the ANSI standard. In other words, the standard guarantees that ANSI C++ programs are portable. In practice most versions of C++ include ANSI C++ as a core language, but also include extra machine-dependent features to allow smooth interaction with different computers' operating systems. These machine dependent features should be used sparingly. Moreover, when parts of a C++ program use non-ANSI components of the language, these should be clearly marked, and as far a possible separated from the rest of the program, so as to make modification of the program for different machines and operating systems as easy as possible.

1.3 The Programming Environment

We need several pieces of software:
  • An editor with which to write and modify the C++ program components or source code,

  •  
  • A compiler with which to convert the source code into machine instructions which can be executed by the computer directly,

  •  
  • A linking program with which to link the compiled program components with each other and with a selection of routines from existing libraries of computer code, in order to form the complete machine-executable object program,

  •  
  • A debugger to help diagnose problems, either in compiling programs in the first place, or if the object program runs but gives unintended results.

  •  

    1.4 An Example C++ Program

    Here is an example of a complete C++ program:
            // The C++ compiler ignores comments which start with  
            // double slashes like this, up to the end of the line.
            
            /* Comments can also be written starting with a slash 
            followed by a star, and ending with a star followed by
             a slash. As you can see, comments written in this way 
            can span more than one line. */
                    
            /* This program prompts the user for the current year, the user's 
            current age, and another year. It then calculates the age
            that the user was or will be in the second year entered. */ 
            
            #include <iostream.h>
                    
            int main() 
            {
                    int year_now, age_now, another_year, another_age; 
            
                    cout << "Enter the current year then press RETURN.\n";
                    cin >> year_now;
            
                    cout << "Enter your current age in years.\n";
                    cin >> age_now;
            
                    cout << "Enter the year for which you wish to know your age.\n";
                    cin >> another_year;
            
                    another_age = another_year - (year_now - age_now);
                    if (another_age >= 0) {
                            cout << "Your age in " << another_year << ": ";
                            cout << another_age << "\n";
                    }
                    else { 
                            cout << "You weren't even born in ";
                            cout << another_year << "!\n";
                    }
            
                    return 0;
            }


    This program illustrates several general features of all C++ programs. It begins (after the comment lines) with the statement

    #include

    This statement is called an include directive. It tells the compiler and the linker that the program will need to be linked to a library of routines that handle input from the keyboard and output to the screen. The header file "iostream.h" contains basic information about this library.

    Because the program is short, it is easily packaged up into a single list of program statements and commands. After the include directive, the basic structure of the program is:

            int main() 
            {
                    First statement;
                    ...
                    ...
                    Last statement;
            
                    return 0;
            }
    All C++ programs have this basic "top-level" structure. Notice that each statement in the body of the program ends with a semicolon. In a well-designed large program, many of these statements will include references or calls to sub-programs, listed after the main program or in a separate file. These sub-programs have roughly the same outline structure as the program here, but there is always exactly one such structure called main. Again, you will learn more about sub-programs later in the course.

    When at the end of the main program, the line

    return 0;

    means "return the value 0 to the computer's operating system to signal that the program has completed successfully". More generally, return statements signal that the particular sub-program has finished, and return a value, along with the flow of control, to the program level above. More about this later.

    Our example program uses four variables:

    year_now, age_now, another_year and another_age

    Program variables are not like variables in mathematics. They are more like symbolic names for "pockets of computer memory" which can be used to store different values at different times during the program execution. These variables are first introduced in our program in the variable declaration

            int year_now, age_now, another_year, another_age;
    which signals to the compiler that it should set aside enough memory to store four variables of type "int" (integer) during the rest of the program execution. Hence variables should always be declared before being used in a program. Indeed, it is considered good style and practice to declare all the variables to be used in a program or sub-program at the beginning. Variables can be one of several different types in C++, and we will discuss variables and types at some length later.

    1.6 Very Simple Input, Output and Assignment

    After we have compiled the program above, we can run it. The result will be something like
            Enter current year then press RETURN.
            1996
            Enter your current age in years.
            36
            Enter the year for which you wish to know your age.
            2001
            Your age in 2001: 41
    The first, third, fifth and seventh lines above are produced on the screen by the program. In general, the program statement
            cout << Expression1 << Expression2 << ... << ExpressionN;
    will produce the screen output
            Expression1Expression2...ExpressionN
    The series of statements
            cout << Expression1; 
            cout << Expression2;
            ... 
            ... 
            cout << ExpressionN;
    will produce an identical output. If spaces or new lines are needed between the output expressions, these have to be included explicitly, with a " " or a "\n" respectively.

    The numbers in bold in the example screen output above have been typed in by the user. In this particular program run, the program statement

            cin >> year_now;
    has resulted in the variable year_now being assigned the value 1996 at the point when the user pressed RETURN after typing in "1996". Programs can also include assignment statements, a simple example of which is the statement
            another_age = another_year - (year_now - age_now);
    Hence the symbol = means "is assigned the value of". ("Equals" is represented in C++ as ==.)

    1.7 Simple Flow of Control

    The last few lines of our example program (other than "return 0") are:
            if (another_age >= 0) {
                    cout << "Your age in " << another_year << ": ";
                    cout << another_age << "\n";
            }
            else { 
                    cout << "You weren't even born in ";
                    cout << another_year << "!\n";
            }
    The "if ... else ..." branching mechanism is a familiar construct in many procedural programming languages. In C++, it is simply called an if statement, and the general syntax is
            if (condition) {
                    Statement1;
                    ...
                    ...
                    StatementN;
            }
            else {
                    StatementN+1;
                    ...
                    ...
                    StatementN+M;
            }
    The "else" part of an "if statement" may be omitted, and furthermore, if there is just one Statement after the "if (condition)", it may be simply written as
            if (condition)
                    Statement;


    It is quite common to find "if statements" strung together in programs, as follows:

            ...
            ...
            if (total_test_score < 50)
                    cout << "You are a failure. You must study much harder.\n";
            else if (total_test_score < 65)
                    cout << "You have just scraped through the test.\n";
            else if (total_test_score < 80)
                    cout << "You have done quite well.\n";
            else if (total_test_score < 95)
                    cout << "Your score is excellent. Well done.\n";
            else {
                    cout << "You cheated!\n";
                    total_test_score = 0;
            }
            ...
            ...
    This program fragment has quite a complicated logical structure, but we can confirm that it is legal in C++ by referring to the syntax diagram for "if statements". In such diagrams, the terms enclosed in ovals or circles refer to program components that literally appear in programs. Terms enclosed in boxes refer to program components that require further definition, perhaps with another syntax diagram. A collection of such diagrams can serve as a formal definition of a programming language's syntax (although they do not help distinguish between good and bad programming style!).

    Below is the syntax diagram for an "if statement". It is best understood in conjunction with the syntax diagram for a "statement" .
    In particular, notice that the diagram doesn't explicitly include the ";" or "{}" delimiters, since these are built into the definition (syntax diagram) of "statement".

    1.7.1 Syntax diagram for an If Statement

    The C++ compiler accepts the program fragment in our example by counting all of the bold text in

            ...
            ...
            if (total_test_score < 50)
                    cout << "You are a failure. You must study much harder.\n";
            else if (total_test_score < 65)
                    cout << "You have just scraped through the test.\n";
            else if (total_test_score < 80)
                    cout << "You have done quite well.\n";
            else if (total_test_score < 95)
                    cout << "Your score is excellent. Well done.\n";
            else {
                    cout << "You cheated!\n";
                    total_test_score = 0;
            }
            ...
            ...
    as the single statement which must follow the first else.

    1.8 Preliminary Remarks about Program Style

    As far as the C++ compiler is concerned, the following program is exactly the same as the program in section 1.5:
        #include <iostream.h> int main() { int year_now, age_now, another_year, another_age; cout << 
    "Enter the current year then press RETURN.\n"; cin >> year_now; 
            cout << "Enter your current age in years.\n"; cin >> age_now; cout << 
        "Enter the year for which you wish to know your age.\n"; cin >> 
        another_year; another_age = another_year - (year_now - age_now); if 
        (another_age >= 0) { cout << "Your age in " << another_year << ": "; 
        cout << another_age << "\n"; } else { cout << 
        "You weren't even born in "; cout << another_year << "!\n"; } return 
        0; }
    However, the lack of program comments, spaces, new lines and indentation makes this program unacceptable. There is much more to developing a good programming style than learning to lay out programs properly, but it is a good start! Be consistent with your program layout, and make sure the indentation and spacing reflects the logical structure of your program. It is also a good idea to pick meaningful names for variables; "year_now", "age_now", "another_year " and "another__age " are better names than "y_n", "a_n", "a_y" and "a_a", and much better than "w", "x", "y" and "z". Remember that your programs might need modification by other programmers at a later date.

    2. Variables, Types and Expressions

    2.1 Identifiers

    As we have seen, C++ programs can be written using many English words. It is useful to think of words found in a program as being one of three types:
    1. Reserved Words. These are words such as if, int and else, which have a predefined meaning that cannot be changed. Here's a more complete list:
    2. asm

      auto

      break

      case

      catch

      char

      class

      const

       

      continue

      default

      delete

      do

      double

      else

      enum

      extern

       

      float

      for

      friend

      goto

      if

      inline

      int

      long

       

      new

      operator

      private

      protected

      public

      register

      return

      short

       

      signed

      sizeof

      static

      struct

      switch

      template

      this

      throw

       

      try

      typedef

      union

      unsigned

      virtual

      void

      volatile

      while

       

    3. Library Identifiers. These words are supplied default meanings by the programming environment, and should only have their meanings changed if the programmer has strong reasons for doing so. Examples are cin, cout and sqrt (square root).

    4.  
    5. Programmer-supplied Identifiers. These words are "created" by the programmer, and are typically variable names, such as year_now and another_age.
    An identifier cannot be any sequence of symbols. A valid identifier must start with a letter of the alphabet or an underscore ("_") and must consist only of letters, digits, and underscores.

    2.2 Data Types

    Integers

    C++ requires that all variables used in a program be given a data type. We have already seen the data type int. Variables of this type are used to represent integers (whole numbers). Declaring a variable to be of type int signals to the compiler that it must associate enough memory with the variable's identifier to store an integer value or integer values as the program executes. But there is a (system dependent) limit on the largest and smallest integers that can be stored. Hence C++ also supports the data types short int and long int which represent, respectively, a smaller and a larger range of integer values than int. Adding the prefix unsigned to any of these types means that you wish to represent non-negative integers only. For example, the declaration
            unsigned short int year_now, age_now, another_year, another_age;
    reserves memory for representing four relatively small non-negative integers.

    Some rules have to be observed when writing integer values in programs:

    1. Decimal points cannot be used; although 26 and 26.0 have the same value, "26.0" is not of type "int".

    2.  
    3. Commas cannot be used in integers, so that (for example) 23,897 has to be written as "23897".

    4.  
    5. Integers cannot be written with leading zeros. The compiler will, for example, interpret "011" as an octal (base 8) number, with value 9.

    Real numbers

    Variables of type "float" are used to store real numbers. Plus and minus signs for data of type "float" are treated exactly as with integers, and trailing zeros to the right of the decimal point are ignored. Hence "+523.5", "523.5" and "523.500" all represent the same value. The computer also excepts real numbers in floating-point form (or "scientific notation"). Hence 523.5 could be written as "5.235e+02" (i.e. 5.235 x 10 x 10), and -0.0034 as "-3.4e-03". In addition to "float", C++ supports the types "double" and "long double", which give increasingly precise representation of real numbers, but at the cost of more computer memory.

    Type Casting

    Sometimes it is important to guarantee that a value is stored as a real number, even if it is in fact a whole number. A common example is where an arithmetic expression involves division. When applied to two values of type int, the division operator "/" signifies integer division, so that (for example) 7/2 evaluates to 3. In this case, if we want an answer of 3.5, we can simply add a decimal point and zero to one or both numbers - "7.0/2", "7/2.0" and "7.0/2.0" all give the desired result. However, if both the numerator and the divisor are variables, this trick is not possible. Instead, we have to use a type cast. For example, we can convert "7" to a value of type double using the expression "double(7)". Hence in the expression
            answer = double(numerator) / denominator
    the "/" will always be interpreted as real-number division, even when both "numerator" and "denominator" have integer values. Other type names can also be used for type casting. For example, "int(14.35)" has an integer value of 14.

    Characters

    Variables of type "char" are used to store character data. In standard C++, data of type "char" can only be a single character (which could be a blank space). These characters come from an available character set which can differ from computer to computer. However, it always includes upper and lower case letters of the alphabet, the digits 0, ... , 9, and some special symbols such as #, £, !, +, -, etc. Perhaps the most common collection of characters is the ASCII character set.

    Character constants of type "char" must be enclosed in single quotation marks when used in a program, otherwise they will be misinterpreted and may cause a compilation error or unexpected program behaviour. For example, "'A'" is a character constant, but "A" will be interpreted as a program variable. Similarly, "'9'" is a character, but "9" is an integer.

    There is, however, an important (and perhaps somewhat confusing) technical point concerning data of type "char". Characters are represented as integers inside the computer. Hence the data type "char" is simply a subset of the data type "int". We can even do arithmetic with characters. For example, the following expression is evaluated as true on any computer using the ASCII character set:

    '9' - 48 == 9

    However, declaring a variable to be of type "char" rather than type "int" makes an important difference as regards the type of input the program expects, and the format of the output it produces. For example, the program

            #include <iostream.h>
            
            int main()
            {
                    int number;
                    char character;
            
                    cout << "Type in a character:\n";
                    cin >> character;
            
                    number = character;
            
                    cout << "The character '" << character;
                    cout << "' is represented as the number ";
                    cout << number << " in the computer.\n";
            
                    return 0;
            }


    produces output such as

            Type in a character:
            9
            The character '9' is represented as the number 57 in the computer.


    We could modify the above program to print out the whole ASCII table of characters using a "for loop". The "for loop" is an example of a repetition statement - we will discuss these in more detail later. The general syntax is:

            for (initialisation; repetition_condition ; update) {
                    Statement1;
                    ...
                    ...
                    StatementN;
            }
    C++ executes such statements as follows: (1) it executes the initialisation statement. (2) it checks to see if repetition_condition is true. If it isn't, it finishes with the "for loop" completely. But if it is, it executes each of the statements Statement1 ... StatementN in turn, and then executes the expression update. After this, it goes back to the beginning of step (2) again.

    Hence to print out the ASCII table, the program above can be modified to:

            #include <iostream.h>
    
            int main()
            {
                    int number;
                    char character;
            
                    for (number = 32 ; number <= 126 ; number = number + 1) {
            
                            character = number;
                            cout << "The character '" << character;
                            cout << "' is represented as the number ";
                            cout << number << " in the computer.\n";
                    }
    
                    return 0;
            }


    which produces the output:

            The character ' ' is represented as the number 32 in the computer.
            The character '!' is represented as the number 33 in the computer.
            ...
            ...
            The character '}' is represented as the number 125 in the computer.
            The character '~' is represented as the number 126 in the computer.

    Strings

    Our example programs have made extensive use of the type "string" in their output. As we have seen, in C++ a string constant must be enclosed in double quotation marks. Hence we have seen output statements such as
    cout << "' is represented as the number ";

    in programs. In fact, "string" is not a fundamental data type such as "int", "float" or "char". Instead, strings are represented as arrays of characters, so we will return to subject of strings later, when we discuss arrays in general.

    User Defined Data Types

    Later in the course we will study the topic of data types in much more detail. We will see how the programmer may define his or her own data types. This facility provides a powerful programming tool when complex structures of data need to be represented and manipulated by a C++ program.
     

    2.3 Some Tips on Formatting Real Number Output

    When program output contains values of type "float", "double" or "long double", we may wish to restrict the precision with which these values are displayed on the screen, or specify whether the value should be displayed in fixed or floating point form. The following example program uses the library identifier "sqrt" to refer to the square root function, a standard definition of which is given in the header file "math.h".
            #include <iostream.h>
            #include <math.h>
            
            int main() 
            {
                    float number;
            
                    cout << "Type in a real number.\n";
                    cin >> number;
                    cout.setf(ios::fixed);    //    LINE 10          
                    cout.precision(2);
                    cout << "The square root of " << number << " is approximately "; 
                    cout << sqrt(number) << ".\n";
            
                    return 0;
            }


    This produces the output

            Type in a real number.
            200
            The square root of 200.00 is approximately 14.14.
    whereas replacing line 10 with "cout.setf(ios::scientific)" produces the output:
            Type in a real number.
            200
            The square root of 2.00e+02 is approximately 1.41e+01.
    We can also include tabbing in the output using a statement such as "cout.width(20)". This specifies that the next item output will have a width of at least 20 characters (with blank space appropriately added if necessary). This is useful in generating tables:
            #include <iostream.h>
            #include <math.h>
            
            int main() 
            {
                    int number;
            
                    cout.width(20);
                    cout << "Number" << "Square Root\n\n";
                    
                    cout.setf(ios::fixed);          
                    cout.precision(2);
                    for (number = 1 ; number <= 10 ; number = number + 1) {
                            cout.width(20);
                            cout << number << sqrt(number) << "\n";
                    }
            
                    return 0;
            }


    This program produces the output

            Number              Square Root
            
            1                   1.00
            2                   1.41
            3                   1.73
            4                   2.00
            5                   2.24
            6                   2.45
            7                   2.65
            8                   2.83
            9                   3.00
            10                  3.16
    (In fact, the above programs work because "cout" is an identifier for an object belonging to the class "stream", and "setf(...)", "precision(...)" and "width(...)" are member functions of "stream". Don't worry too much about this for now - you will learn more about objects, classes and member functions later in the object-oriented part of the course.)

    2.4 Declarations, Constants and Enumerations

    As we have already seen, variables have to be declared before they can be used in a program, using program statements such as
            float number;
    Between this statement and the first statement which assigns "number" an explicit value, the value contained in the variable "number" is arbitrary. But in C++ it is possible (and desirable) to initialise variables with a particular value at the same time as declaring them. Hence we can write
            float PI = 3.1416;
    Furthermore, we can specify that a variable's value cannot be altered during the execution of a program with the reserved word "const":

    Enumerations

    Constants of type "int" may also be declared with an enumeration statement. For example, the declaration
            enum { MON, TUES, WED, THURS, FRI, SAT, SUN };
    is shorthand for
            const int MON = 0;
            const int TUES = 1;
            const int WED = 2;
            const int THURS = 3;
            const int FRI = 4;
            const int SAT = 5;
            const int SUN = 6;
    By default, members of an "enum" list are given the values 0, 1, 2, etc., but when "enum" members are explicitly initialised, uninitialised members of the list have values that are one more than the previous value on the list:
            enum { MON = 1, TUES, WED, THURS, FRI, SAT = -1, SUN };
    In this case, the value of "FRI" is 5, and the value of "SUN" is 0.
     

    Where to put Constant and Variable Declarations

    Generally speaking, it is considered good practice to put constant declarations before the "main" program heading, and variable declarations afterwards, in the body of "main". For example, the following program draws a circle of a given radius on the screen, and then prints out its circumference:
            #include <iostream.h>
            
            const float PI = 3.1416;
            const float SCREEN_WIDTH = 317.24;
            
            int drawCircle(float diameter); /* this is a "function prototype" */
            
            int main() 
            {
                    float radius = 0;
            
                    cout << "Type in the radius of the circle.\n";
                    cin >> radius;
            
                    drawCircle(radius * 2);
            
                    cout.setf(ios::fixed);          
                    cout.precision(2);
                    cout << "The circumference of a circle of radius " << radius; 
                    cout << " is approximately " << 2 * PI * radius << ".\n";
    
                    return 0;
            }
            
            int drawCircle(float diameter)
            {
                    float radius = 0;
            
                    if (diameter > SCREEN_WIDTH) 
                            radius = SCREEN_WIDTH / 2.0;
                    else
                            radius = diameter / 2.0;
                    ...
                    ...
            }
    Ater the definition of "main()", this program includes a definition of the function "drawCircle(...)", the details of which need not concern us here (we can simply think of "drawCircle(...)" as a function like "sqrt(...)"). But notice that although both "main()" and "drawCircle(...)" use the identifier "radius", this refers to a different variable in "main()" than in "drawCircle(...)". Had a variable "radius" been declared before the "main" program heading, it would have been a public or global variable. In this case, and assuming there was no other variable declaration inside the function "drawCircle(...)", if "drawCircle(...)" had assigned it the value "SCREEN_WIDTH / 2.0", "main()" would have subsequently printed out the wrong value for the circumference of the circle. We say that the (first) variable "radius" is local to the main part of the program, or has the function mainas its scope. In contrast, it usually makes sense to make constants such as "PI" and "SCREEN_WIDTH" global, i.e. available to every function.

    In any case, notice that the program above incorporates the safety measure of echoing the input. In other words, the given value of "radius" is printed on the screen again, just before the circumference of the circle is output.
     

    2.5 Assignments and Expressions

    Shorthand Arithmetic Assignment Statements

    We have already seen how programs can include variable assignments such as
            number = number + 1;
    Since it is often the case that variables are assigned a new value in function of their old value, C++ provides a shorthand notation. Any of the operators "+" (addition), "-" (subtraction), "*" (multiplication), "/" (division) and "%" (modulus) can be prefixed to the assignment operator (=), as in the following examples:
     
    Example:

     number += 1;

    total -= discount;

    bonus *= 2;

    time /= rush_factor;

    change %= 100;

    amount *= count1 + count2;

    Equivalent to:

    number = number + 1;

    total = total - discount;

    bonus = bonus * 2;

    time = time / rush_factor;

    change = change % 100;

    amount = amount * (count1 + count2);

    The first of the above examples may written in even shorter form. Using the increment operator "++", we may simply write

            number++;
    The operator "++" may also be used as a prefix operator:
            ++number;
    but care must be taken, since in some contexts the prefix and postfix modes of use have different effects. For example, the program fragment
            x = 4;
            y = x++;
    results in "x" having the value 5 and "y" having the value 4, whereas
            x = 4;
            y = ++x;
    results in both variables having value 5. This is because "++x" increments the value of "x" before its value is used, whereas "x++" increments the value afterwards. There is also an operator "--", which decrements variables by 1, and which can also be used in prefix or postfix form.

    In general, assignment statements have a value equal to the value of the left hand side after the assignment. Hence the following is a legal expression which can be included in a program and which might be either evaluated as true or as false:

            (y = ++x) == 5
    It can be read as the assertion: "after x is incremented and its new value assigned to y, y's value is equal to 5".

    Boolean Expressions and Operators

    Intuitively, we think of expressions such as "2 < 7", "1.2 != 3.7" and "6 >= 9" as evaluating to "true" or "false" ("!=" means "not equal to"). Such expressions can be combined using the logical operators "&&" ("and"), "||" ("or") and "!" ("not"), as in the following examples:
     
    Expression:

     (6 <= 6) && (5 < 3)

    (6 <= 6) || (5 < 3)

    (5 != 6)

    (5 < 3) && (6 <= 6) || (5 != 6)

    (5 < 3) && ((6 <= 6) || (5 != 6))

    !((5 < 3) && ((6 <= 6) || (5 != 6)))

    True or False:

     false

    true

    true

    true

    false

    true

    The fourth of these expressions is true because the operator "&&" has a higher precedence than the operator "||". You can check the relative precedence of the different C++ operators in a C++ programming manual or text book. But if in doubt use ( ) parentheses, which in any case often make the program easier to read.

    Compound Boolean expressions are typically used as the condition in "if statements" and "for loops". For example:

            ...
            ...
            if (total_test_score >= 50 && total_test_score < 65)
                    cout << "You have just scraped through the test.\n";
            ...
            ...
    Once again, there is an important technical point concerning Boolean expressions. In C++, "true" is represented simply as the value 1 (or any positive integer in some systems), and "false" is represented as the value 0. This can lead to errors. For example, it is quite easy to type "=" instead of "==". Unfortunately, the program fragment
            ...
            ...
            if (number_of_people = 1)
                    cout << "There is only one person.\n";
            ...
            ...
    will always result in the message "There is only one person" being output to the screen, even if the previous value of the variable "number_of_people" was not 1.
     
     

    3. Functions and Procedural Abstraction

    3.1 The Need for Sub-programs

    A natural way to solve large problems is to break them down into a series of sub-problems, which can be solved more-or-less independently and them combined to arrive at a complete solution. In programming, this methodology reflects itself in the use of sub-programs, and in C++ all sub-programs are called functions (corresponding to both "functions" and "procedures" in Pascal and some other programming languages).

    We have already been using sub-programs. For example, in the program which generated a table of square roots, we used the following "for loop":

            ...
            #include<math.h>
            ...     
            ...
                    for (number = 1 ; number <= 10 ; number = number + 1) {
                            cout.width(20);
                            cout << number << sqrt(number) << "\n";
                    }
            ...
            ...
    The function "sqrt(...)" is defined in a sub-program accessed via the library file "math.h". The sub-program takes "number", uses a particular algorithm to compute its square root, and then returns the computed value back to the program. We don't care what the algorithm is as long as it gives the correct result. It would be ridiculous to have to explicitly (and perhaps repeatedly) include this algorithm in the "main" program.

    In this chapter we will discuss how the programmer can define his or her own functions. At first, we will put these functions in the same file as "main". Later we will see how to place different functions in different files.
     

    3.2 User-defined Functions

    Here's a trivial example of a program which includes a user defined function, in this case called "area(...)". The program computes the area of a rectangle of given length and width.
            #include<iostream.h>
            
            int area(int length, int width);                /* function declaration */
            
            /* MAIN PROGRAM: */
            int main()
            {
                    int this_length, this_width;      
            
                    cout << "Enter the length: ";             /* <--- line 9 */
                    cin >> this_length;
                    cout << "Enter the width: ";
                    cin >> this_width;
                    cout << "\n";                             /* <--- line 13 */
            
                    cout << "The area of a " << this_length << "x" << this_width;
                    cout << " rectangle is " << area(this_length, this_width);
            
                    return 0;
            }
            /* END OF MAIN PROGRAM */
            
            /* FUNCTION TO CALCULATE AREA: */
            int area(int length, int width)   /* start of function definition */
            {
                    int number;
            
                    number = length * width;
            
                    return number;
            }                                 /* end of function definition */
            /* END OF FUNCTION */


    Although this program is not written in the most succinct form possible, it serves to illustrate a number of features concerning functions:

    A function may have more than one "return" statement, in which case the function definition will end execution as soon as the first "return" is reached. For example:
            double absolute_value(double number)
            {
                    if (number >= 0)
                            return number;
                    else
                            return 0 - number;
            }

    3.3 Value and Reference Parameters

    The parameters in the functions above are all value parameters. When the function is called within the main program, it is passed the values currently contained in certain variables. For example, "area(...)" is passed the current values of the variables "this_length" and "this_width". The function "area(...)" then stores these values in its own private variables, and uses its own private copies in its subsequent computation.

    Functions which use Value Parameters are Safe

    The idea of value parameters makes the use of functions "safe", and leads to good programming style. It helps guarantee that a function will not have hidden side effects. Here is a simple example to show why this is important. Suppose we want a program which produces the following dialogue:
            Enter a positive integer:
            4
            The factorial of 4 is 24, and the square root of 4 is 2.


    It would make sense to use the predefined function "sqrt(...)" in our program, and write another function "factorial(...)" to compute the factorial n! = (1 x 2 x ... x n) of any given positive integer n. Here's the complete program:

            #include<iostream.h>
            #include<math.h>
            
            int factorial(int number);
            
            /* MAIN PROGRAM: */
            int main()
            {
                    int whole_number;      
            
                    cout << "Enter a positive integer:\n";
                    cin >> whole_number;
                    cout << "The factorial of " << whole_number << " is ";
                    cout << factorial(whole_number);
                    cout << ", and the square root of " << whole_number << " is ";
                    cout << sqrt(whole_number) << ".\n";
    
                    return 0;
            }
            /* END OF MAIN PROGRAM */
            
            /* FUNCTION TO CALCULATE FACTORIAL: */
            int factorial(int number)
            {
                    int product = 1;
            
                    for ( ; number > 0 ; number--)
                            product *= number;
            
                    return product;
            }
            /* END OF FUNCTION */


    By the use of a value parameter, we have avoided the (correct but unwanted) output

            Enter a positive integer:
            4
            The factorial of 4 is 24, and the square root of 1 is 1.
    which would have resulted if the function "factorial(...)" had permanently changed the value of the variable "whole_number".
     

    Reference Parameters

    Under some circumstances, it is legitimate to require a function to modify the value of an actual parameter that it is passed. For example, going back to the program which inputs the dimensions of a rectangle and calculates the area, it would make good design sense to package up lines 9 to 13 of the main program into a "get-dimensions" sub-program (i.e. a C++ function). In this case, we require the function to alter the values of "this_length" and "this_width" (passed as parameters), according to the values input from the keyboard. We can achieve this as follows using reference parameters, whose types are post-fixed with an "&":
           #include<iostream.h>

            int area(int length, int width);

            void get_dimensions(int& length, int& width);

            /* MAIN PROGRAM: */
            int main()
            {
                    int this_length, this_width;

                    get_dimensions(this_length, this_width);
                    cout << "The area of a " << this_length << "x" << this_width;
                    cout << " rectangle is " << area(this_length, this_width);

                    return 0;
            }
            /* END OF MAIN PROGRAM */

            /* FUNCTION TO INPUT RECTANGLE DIMENSIONS: */
            void get_dimensions(int& length, int& width)
            {
                    cout << "Enter the length: ";
                    cin >> length;
                    cout << "Enter the width: ";
                    cin >> width;
                    cout << "\n";
            }
            /* END OF FUNCTION */

            /* FUNCTION TO CALCULATE AREA: */
            int area(int length, int width)
            {
                    return length * width;
            }
            /* END OF FUNCTION */

    Notice that, although the function "get_dimensions" permanently alters the values of the parameters "this_length" and "this_width", it does not return any other value (i.e. is not a "function" in the mathematical sense). This is signified in both the function declaration and the function heading by the reserved word "void".

    3.4 Polymorphism and Overloading

    C++ allows polymorphism, i.e. it allows more than one function to have the same name, provided all functions are either distinguishable by the typing or the number of their parameters. Using a function name more than once is sometimes referred to as overloading the function name. Here's an example:
            #include<iostream.h>
            
            int average(int first_number, int second_number, int third_number);
            
            int average(int first_number, int second_number);
            
            /* MAIN PROGRAM: */
            int main()
            {
                    int number_A = 5, number_B = 3, number_C = 10;      
            
                    cout << "The integer average of " << number_A << " and ";
                    cout << number_B << " is ";
                    cout << average(number_A, number_B) << ".\n\n";
            
                    cout << "The integer average of " << number_A << ", ";
                    cout << number_B << " and " << number_C << " is ";
                    cout << average(number_A, number_B, number_C) << ".\n";
                    
                    return 0;
            }
            /* END OF MAIN PROGRAM */
            
            /* FUNCTION TO COMPUTE INTEGER AVERAGE OF 3 INTEGERS: */
            int average(int first_number, int second_number, int third_number)
            {
                    return ((first_number + second_number + third_number) / 3);
            }  
            /* END OF FUNCTION */
            
            /* FUNCTION TO COMPUTE INTEGER AVERAGE OF 2 INTEGERS: */
            int average(int first_number, int second_number)
            {
                    return ((first_number + second_number) / 2);
            }  
            /* END OF FUNCTION */


    This program produces the output:

            The integer average of 5 and 3 is 4.
            
            The integer average of 5, 3 and 10 is 6.

    3.5 Procedural Abstraction and Good Programming Style

    One of the main purposes of using functions is to aid in the top down design of programs. During the design stage, as a problem is subdivided into tasks (and then into sub-tasks, sub-sub-tasks, etc.), the problem solver (programmer) should have to consider only what a function is to do and not be concerned about the details of the function. The function name and comments at the beginning of the function should be sufficient to inform the user as to what the function does. (Indeed, during the early stages of program development, experienced programmers often use simple "dummy" functions or stubs, which simply return an arbitrary value of the correct type, to test out the control flow of the main or higher level program component.)

    Developing functions in this manner is referred to as functional or procedural abstraction. This process is aided by the use of value parameters and local variables declared within the body of a function. Functions written in this manner can be regarded as "black boxes". As users of the function, we neither know nor care why they work.

    3.6 Splitting Programs into Different Files

    As we have seen, C++ makes heavy use of predefined standard libraries of functions, such as "sqrt(...)". In fact, the C++ code for "sqrt(...)", as for most functions, is typically split into two files: It is easy to extend this library structure to include files for user-defined functions, such as "area(...)", "factorial(...)" and "average(...)".

    The code in the main program file is as follows:

            #include<iostream.h>
            #include"averages.h"
            
            int main()
            {
                    int number_A = 5, number_B = 3, number_C = 10;
                    
                    cout << "The integer average of " << number_A << " and ";
                    cout << number_B << " is ";
                    cout << average(number_A, number_B) << ".\n\n";
                    
                    cout << "The integer average of " << number_A << ", ";
                    cout << number_B << " and " << number_C << " is ";
                    cout << average(number_A, number_B, number_C) << ".\n";
                    
                    return 0;
            }


    Notice that whereas "include" statements for standard libraries such as "iostream.h" delimit the file name with angle ("<>") brackets, the usual convention is to delimit user-defined library file names with double quotation marks - hence the line " #include"averages.h" " in the listing above.

    The code in the header file ">averages.h>" is listed below. Notice the use of the file identifier "AVERAGES_H", and the reserved words "ifndef" ("if not defined"), "define", and "endif". "AVERAGES_H" is a (global) symbolic name for the file. The first line and last two lines of code ensure that the compiler (in fact, the preprocessor) only works through the code in between once, even if the line " #include"averages.h" " is included in more than one other file.

    Constant and type definitions are also often included in header files. You will learn more about this in the object-oriented part of the course.

            #ifndef AVERAGES_H
            
            /* (constant and type definitions could go here) */
            
            /* FUNCTION TO COMPUTE INTEGER AVERAGE OF 3 INTEGERS: */
            int average(int first_number, int second_number, int third_number);
            
            /* FUNCTION TO COMPUTE INTEGER AVERAGE OF 2 INTEGERS: */
            int average(int first_number, int second_number);
            
            #define AVERAGES_H
            #endif


    Finally, the code in the implementation file "averages.cp" is as follows:

            #include<iostream.h>
            #include"averages.h"
            
            /* FUNCTION TO COMPUTE INTEGER AVERAGE OF 3 INTEGERS: */
            int average(int first_number, int second_number, int third_number)
            {
                    return ((first_number + second_number + third_number) / 3);
            }
            /* END OF FUNCTION */
            
            /* FUNCTION TO COMPUTE INTEGER AVERAGE OF 2 INTEGERS: */
            int average(int first_number, int second_number)
            {
                    return ((first_number + second_number) / 2);
            }
            /* END OF FUNCTION */


    Note the modularity of this approach. We could change the details of the code in "averages.cp" without making any changes to the code in "averages.h" or in the main program file.
     

    4. Files and Streams

    4.1. Why Use Files?

    All the programs we have looked at so far use input only from the keyboard, and output only to the screen. If we were restricted to use only the keyboard and screen as input and output devices, it would be difficult to handle large amounts of input data, and output data would always be lost as soon as we turned the computer off. To avoid these problems, we can store data in some secondary storage device, usually magnetic tapes or discs. Data can be created by one program, stored on these devices, and then accessed or modified by other programs when necessary. To achieve this, the data is packaged up on the storage devices as data structures called files.

    The easiest way to think about a file is as a linear sequence of characters. In a simplifed picture (which ignores special characters for text formatting) these lecture notes might be stored in a file called "Lecture_4" as:

    Figure 4.1.1


    4.2 Streams

    Before we can work with files in C++, we need to become acquainted with the notion of a stream. We can think of a stream as a channel or conduit on which data is passed from senders to receivers. As far as the programs we will use are concerned, streams allow travel in only one direction. Data can be sent out from the program on an output stream, or received into the program on an input stream. For example, at the start of a program, the standard input stream "cin" is connected to the keyboard and the standard output stream "cout" is connected to the screen.

    In fact, input and output streams such as "cin" and "cout" are examples of (stream) objects. So learning about streams is a good way to introduce some of the syntax and ideas behind the object-oriented part of C++. The header file which lists the operations on streams both to and from files is called "fstream.h". We will therefore assume that the program fragments discussed below are embedded in programs containing the "include" statement

    #include<fstream.h>

    As we shall see, the essential characteristic of stream processing is that data elements must be sent to or received from a stream one at a time, i.e. in serial fashion.
     

    Creating Streams

    Before we can use an input or output stream in a program, we must "create" it. Statements to create streams look like variable declarations, and are usually placed at the top of programs or function implementations along with the variable declarations. So for example the statements
            ifstream in_stream; 
            ofstream out_stream;
    respectively create a stream called "in_stream" belonging to the class (like type) "ifstream" (input-file-stream), and a stream called "out_stream" belonging to the class "ofstream" (output-file-stream). However, the analogy between streams and ordinary variables (of type "int", "char", etc.) can't be taken too far. We cannot, for example, use simple assignment statements with streams (e.g. we can't just write "in_stream1 = in_stream2").
     

    Connecting and Disconnecting Streams to Files

    Having created a stream, we can connect it to a file using the member function "open(...)". (We have already come across some member functions for output streams, such as "precision(...)" and "width(...)", in section 2.) The function "open(...)" has a different effect for ifstreams than for ofstreams (i.e. the function is polymorphic).

    To connect the ifstream "in_stream" to the file "Lecture_4", we use the following statement:

            in_stream.open("Lecture_4");
    This connects "in_stream" to the beginning of "Lecture_4". Diagramatically, we end up in the following situation:

    Figure 4.2.1

    To connect the ofstream "out_stream" to the file "Lecture_4", we use an analogous statement:

            out_stream.open("Lecture_4");
    Although this connects "out_stream" to "Lecture_4", it also deletes the previous contents of the file, ready for new input. Diagramatically, we end up as follows:

    Figure 4.2.2

    To disconnect connect the ifstream "in_stream" to whatever file it is connected to, we write:

            in_stream.close();
    Diagramatically, the situation changes from that of Figure 4.2.1 to:

    Figure 4.2.3

    The statement:

            out_stream.close();
    has a similar effect, but in addition the system will "clean up" by adding an "end-of-file" marker at the end of the file. Thus, if no data has been output to "Lecture_4" since "out_stream" was connected to it, we change from the situation in Figure 4.2.2 to:

    Figure 4.2.4

    In this case, the file "Lecture_4" still exists, but is empty.
     
     
     
     
     

    4.3 Checking for Failure with File Commands

    File operations, such as opening and closing files, are a notorious source of errors. Robust commercial programs should always include some check to make sure that file operations have completed successfully, and error handling routines in case they haven't. A simple checking mechanism is provided by the member function "fail()". The function call
            in_stream.fail();
    returns TRUE if the previous stream operation on "in_stream" was not successful (perhaps we tried to open a file which didn't exist). If a failure has occurred, "in_stream" may be in a corrupted state, and it is best not to attempt any more operations with it. The following example program fragment plays very safe by quitting the program entirely, using the "exit(1)" command from the library "stdlib.h":
            #include <iostream.h>
            #include <fstream.h>
            #include <stdlib.h>
            
            int main()
            {
                    ifstream in_stream;
            
                    in_stream.open("Lecture_4");
                    if (in_stream.fail())
                    {
                            cout << "Sorry, the file couldn't be opened!\n";
                            exit(1);
                    }
                    ...
     

    4.4 Character Input and Output

     

    Input using "get(...)"

    Having opened an input file, we can extract or read single characters from it using the member function "get(...)". This function takes a single argument of type "char". If the program is in the state represented in Figure 4.2.1, the statement
            in_stream.get(ch);
    has two effects: (i) the variable "ch" is assigned the value "'4'", and (ii) the ifstream "in_stream" is re- positioned so as to be ready to input the next character in the file. Diagramatically, the new situation is:

    Figure 4.4.1

    Output using "put(...)"

    We can input or write single characters to a file opened via an ofstream using the member function "put(...)". Again, this function takes a single argument of type "char". If the program is in the state represented in Figure 4.2.2, the statement
            out_stream.put('4');
    changes the state to:

    Figure 4.4.2

    The "putback(...)" Function

    C++ also includes a "putback(...)" function for ifstreams. This doesn't really "put the character back" (it doesn't alter the actual input file), but behaves as if it had. Diagramatically, if we started from the state in Figure 4.3.1, and executed the statement
            in_stream.putback(ch);
    we would end up in the state:

    Figure 4.4.3

    Indeed, we can "putback" any character we want to. The alternative statement

            in_stream.putback('7');
    would result in:

    Figure 4.4.4


    4.5 Checking for the End of an Input File

     

    4.5.1 End-of-file Checking For Systems Which Implement "eof()"

    Special care has to be taken with input when the end of a file is reached. Most versions of C++ incorporate an end-of-file (EOF) flag, and a member function called "eof()" for ifstreams to test if this flag is set to TRUE or FALSE. It's worth discussing such systems briefly, since many text books assume this useful facility.

    In such systems, when an ifstream is initially connected to a file, the EOF flag is set to FALSE (even if the file is empty). However, if the ifstream "in_stream" is positioned at the end of a file, and the EOF flag is FALSE, the statement

            in_stream.get(ch);
    will leave the variable "ch" in an unpredictable state, and set the EOF flag to TRUE. Once the EOF flag is set to TRUE, no attempt should be made to read from the file, since the results will be unpredictable. To illustrate with a diagramatic example, if we start from

    Figure 4.5.1

    and then execute the statement

            in_stream.get(ch);
    this results in the state

    Figure 4.5.2

    If we again execute the statement

            in_stream.get(ch);
    this now results in the state

    Figure 4.5.3

    The boolean expression

            in_stream.eof()
    will now evaluate to TRUE.
     

    Below is a simple program which uses these techniques to copy the file "Lecture_4" simultaneously to the screen and to the file "Copy_of_4". Note the use of a while loop in this program. "While" loops are simplified versions of "for" loops, without the initialisation and update statements in the "()" parentheses (we will look at such statements again in the next section).
     

            #include <iostream.h>
            #include <fstream.h>
            
            int main()
            {
                    char character;
                    ifstream in_stream;
                    ofstream out_stream;
            
                    in_stream.open("Lecture_4");
                    out_stream.open("Copy_of_4");
            
                    in_stream.get(character);
                    while (!in_stream.eof())
                    {
                            cout << character;
                            out_stream.put(character);
                            in_stream.get(character);
                    }
    
                    out_stream.close();
                    in_stream.close();
            
                    return 0;
            }
     

    4.5.2 End-of-file Checking For Systems Which Don't Implement "eof()"

    Unfortunately, some C++ systems  don't include proper facilities for setting and checking the EOF flag. Under such circumstances, we are forced to use the "fail()" function to check for the end of the file. This is rather unsatisfactory, because we end up with a stream in a corrupted state which we have to avoid using again. In terms of our diagrammatic example, diagrams 4.5.1 and 4.5.2 are the same, except that the value of the EOF flag is undefined. However, from state 4.5.2, executing the statement
            in_stream.get(ch);


    Figure 4.5.3

    This doesn't matter too much, as long as we are careful not to use "in_stream" for any more operations. For such systems, the program can be rewritten as
     

            #include <iostream.h>
            #include <fstream.h>
            
            int main()
            {
                    char character;
                    ifstream in_stream;
                    ofstream out_stream;
            
                    in_stream.open("Lecture_4");
                    out_stream.open("Copy_of_4");
            
                    in_stream.get(character);
                    while (!in_stream.fail())
                    {
                            cout << character;
                            out_stream.put(character);
                            in_stream.get(character);
                    }
            
                    out_stream.close();
                    in_stream.close();
            
                    return 0;
            }

    4.6 Streams as Arguments in Functions

    Streams can be arguments to functions, but must be reference parameters (not value parameters). Below is another version of the program which uses the function "copy_to(...)".
            #include <iostream.h>
            #include <fstream.h>
            
            void copy_to(ifstream& in, ofstream& out);
            
            /* MAIN PROGRAM: */
            int main()
            {
                    ifstream in_stream;
                    ofstream out_stream;
            
                    in_stream.open("Lecture_4");
                    out_stream.open("Copy_of_4");
                    copy_to(in_stream, out_stream);
                    out_stream.close();
                    in_stream.close();
            
                    return 0;
            }
            /* END OF MAIN PROGRAM */
            
            /* FUNCTION TO COPY A FILE TO ANOTHER FILE AND TO THE SCREEN: */
            void copy_to(ifstream& in, ofstream& out)
            {
                    char character;
            
                    in.get(character);
                    while (!in.fail())
                    {
                            cout << character;
                            out.put(character);
                            in.get(character);
                    }
            }
            /* END OF FUNCTION */
     

    4.7 Input and Output Using ">>" and "<<"

    So far we have only talked about writing and reading individual characters to and from files. At the lowest level, ofstreams and ifstreams only deal with files which are sequences of characters. So data of other types ("int", "double", etc.) has to be converted into character sequences before it can be written to a file, and these character sequences have to be converted back again when they are input.

    However, the operators ">>" and "<<", which we have already met in the context of keyboard input and screen output, do some of this conversion automatically. For example, from the state

    Figure 4.7.1

    execution of the statement

            out_stream << 437 << ' ';
    will result in the new state

    Figure 4.7.2

    When using these higher level facilities, it is important to include at least one "' '" (blank) character (or a new-line character) after each item of data written. This ensures that the data items are correctly separated in the file, ready for input using ">>". For example, from the state

    Figure 4.7.3

    where "n" has been declared as of data type "int", execution of the statement

            in_stream >> n;
    will result in the new state

    Figure 4.7.3

    Notice that the operation ">>" has skipped over the blank space "' '" before the number 437. It always does this, no matter what data type it has been reading or expects to read (even characters).

    The following program, which first creates a file called "Integers" containing the integers 51, 52, 53, 54 and 55, further illustrates these techniques.

            #include <iostream.h>
            #include <fstream.h>
            
            int main()
            {
                    char character;
                    int number = 51;
                    int     count = 0;
                    ofstream out_stream;
                    ifstream in_stream1;   /* Stream for counting integers. */
                    ifstream in_stream2;   /* Stream for counting characters. */
            
                    /* Create the file */
                    out_stream.open("Integers");
                    for (count = 1 ; count <= 5 ; count++)
                            out_stream << number++ << ' ';
                    out_stream.close();
            
                    /* Count the integers in the file */
                    in_stream1.open("Integers");
                    count = 0;
                    in_stream1 >> number;
                    while (!in_stream1.fail())
                    {
                            count++;
                            in_stream1 >> number;
                    }
                    in_stream1.close();
                    cout << "There are " << count << " integers in the file,\n";
            
                    /* Count the non-blank characters */
                    in_stream2.open("Integers");
                    count = 0;
                    in_stream2 >> character;
                    while (!in_stream2.fail())
                    {
                            count++;
                            in_stream2 >> character;
                    }
                    in_stream2.close();
                    cout << "represented using " << count << " characters.\n";
            
                    return 0;
            }


    This program produces the following output:

            There are 5 integers in the file,
            represented using 10 characters.
    Once again, notice that, unlike the function "get(...)", the operator ">>" has jumped over the blank (i.e. space) characters in the file (which separate the five integers) when used in counting characters in the last part of the program.
     
     

    5. Branch and Loop Statements

    5.1 Boolean Values, Expressions and Functions

    In this lecture we will look more closely at branch and loop statements such as "for" and "while" loops and "if ... else" statements. All these constructs involve the evaluation of one or more logical (or "Boolean") expressions, and so we begin by looking at different ways to write such expressions.

    As we have seen, in reality C++ represents "True" as the integer 1, and "False" as 0. However, expressions such as

    condition1 == 1

    or

    condition2 = 0

    aren't particularly clear - it would be better to be able to follow our intuition and write

    condition1 == True

    and

    condition2 = False

    Furthermore, it is desirable to have a seperate type for variables such as "condition1", rather than having to declare them as of type "int". We can achieve all of this with a named enumeration:

            enum Logical {False, True}
    which is equivalent to
            enum Logical {False = 0, True = 1}
    This line acts a kind of type definition for a new data type "Logical", so that lower down the program we can add variable declarations such as:
            Logical condition1, condition2;
    Indeed, we can now use the identifier "Logical" in exactly the same way as we use the identifiers "int", "char", etc. In particular, we can write functions which return a value of type "Logical". The following example program takes a candidate's age and test score, and reports whether the candidate has passed the test. It uses the following criteria: candidates between 0 and 14 years old have a pass mark of 50%, 15 and 16 year olds have a pass mark of 55%, over 16's have a pass mark of 60%:
            #include <iostream.h>
            
                    enum Logical {False, True};
            
                    Logical acceptable(int age, int score);
            
            /* START OF MAIN PROGRAM */
            int main() 
            {
                    int candidate_age, candidate_score;
            
                    cout << "Enter the candidate's age: ";
                    cin >> candidate_age;
                    cout << "Enter the candidate's score: ";
                    cin >> candidate_score;
            
                    if (acceptable(candidate_age, candidate_score))
                            cout << "This candidate passed the test.\n";
                    else
                            cout << "This candidate failed the test.\n";
            
                    return 0;
            }
            /* END OF MAIN PROGRAM */
    
            /* FUNCTION TO EVALUATE IF TEST SCORE IS ACCEPTABLE */
            Logical acceptable(int age, int score)
            {
                    if (age <= 14 && score >= 50)
                            return True;
                    else if (age <= 16 && score >= 55)
                            return True;
                    else if (score >= 60)
                            return True;
                    else
                            return False;
            }
            /*END OF FUNCTION */


    Note that since "True" and "False" are constants, it makes sense to declare them outside the scope of the main program, so that the type "Logical" can be used by every function in the file. An alternative way to write the above function "acceptable(...)" would be:

            /* FUNCTION TO EVALUATE IF TEST SCORE IS ACCEPTABLE */
            Logical acceptable(int age, int score)
            {
                    Logical passed_test = False;
            
                    if (age <= 14 && score >= 50)
                            passed_test = True;
                    else if (age <= 16 && score >= 55)
                            passed_test = True;
                    else if (score >= 60)
                            passed_test = True;
                    
                    return passed_test;
            }
        &