Introduction to C++

Evolution

cppevolution.gif

"C++ was designed so that the author and his friends would not have to program in Assembler, C, or various modern high-level languages. Its main purpose is to make writing good programs easier and more pleasant for the individual programmer." -- Bjarne Stroustrup

Overview

Hello, World

// ---------------------------------------------------------------------------
// hello.cpp
//
// Writes "Hello, World" to standard output on a single line.
// ---------------------------------------------------------------------------

#include <iostream>

int main() {
    std::cout << "Hello, World\n";
    return 0;
}

To compile this, just enter

    gcc hello.cpp

This produces a.exe on Windows systems and a.out on most other systems. Or use an IDE! You can find several on the web.

On some systems, you may have to use g++ instead of gcc.

Another Example Program

Here is a little program that prompts you for an integer then displays it with roman numerals. It illustrates the using namespace directive, which frees you from having to put std:: everywhere!

// ---------------------------------------------------------------------------
// roman.cpp
//
// This is a simple commandline program which prompts the user for an integer
// input then repsonds with the number that the program thinks was entered
// as a roman numeral.
// ---------------------------------------------------------------------------

#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;

string toRoman(int n) {
    if (n <= 0) {throw out_of_range("No Roman equivalent");}
    string numeral;
    while (n >= 1000) {numeral += "M"; n -= 1000;}
    if (n >= 900) {numeral += "CM"; n -= 900;}
    if (n >= 500) {numeral += "D"; n -= 500;}
    if (n >= 400) {numeral += "CD"; n -= 400;}
    while (n >= 100) {numeral += "C"; n -= 100;}
    if (n >= 90) {numeral += "XC"; n -= 90;}
    if (n >= 50) {numeral += "L"; n -= 50;}
    if (n >= 40) {numeral += "XL"; n -= 40;}
    while (n >= 10) {numeral += "X"; n -= 10;}
    if (n >= 9) {numeral += "IX"; n -= 9;}
    if (n >= 5) {numeral += "V"; n -= 5;}
    if (n >= 4) {numeral += "IV"; n -= 4;}
    while (n >= 1) {numeral += "I"; n -= 1;}
    return numeral;
}

int main() {
    int n;
    cout << "Enter an integer:\n";
    cin >> n;
    try {
        cout << n << " is " << toRoman(n) << '\n';
        return 0;
    } catch (out_of_range e) {
        cout << "Can't process " << n << ": " << e.what() << "\n";
        return -1;
    }
}

More Programming Examples

Here is a trivial application for finding prime numbers.

// ---------------------------------------------------------------------------
// primes.cpp
//
// This program displays all the prime numbers up to and including 1000,
// using the famous algorithm of Erathostenes.
// ---------------------------------------------------------------------------

#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;

// The sieve is an array of 1001 bool values, indexed from 0 to 1000, all
// initialized to the value true.

vector<bool> sieve(1001, true);

// This function writes false in each slot of the vector corresponding to
// a composite number.  First, 0 and 1 are composite by definition.  Then
// for each value starting with 2, if the value is still thought to be
// prime, we write false in each slot corresponding to its multiples.

void checkOffComposites(vector<bool>& s) {
    s[0] = false;
    s[1] = false;
    for (int i = 2; i * i < s.size(); i++) {
        if (s[i]) {
            for (int j = i + i; j < s.size(); j += i) {
                s[j] = false;
            }
        }
    }
}

// This function writes out all the values which correspond to positions
// in a vector containing the value true.  Each value is written to the
// standard output in a field of 8 characters.

void displayTrueIndices(const vector<bool>& s) {
    for (int i = 0; i < s.size(); i++) {
        if (s[i]) {
            cout << setw(8) << i;
        }
    }
    cout << '\n';
}

// main() just calls the two functions.

int main() {
    checkOffComposites(sieve);
    displayTrueIndices(sieve);
    return 0;
}

Here is an illustration of the map class from the Standard Library.

// ---------------------------------------------------------------------------
// colors.cpp
//
// This program asks the user how to say a particular primary color
// in Spanish then checks the user's input to see if it is correct.
// ---------------------------------------------------------------------------

#include <iostream>
#include <string>
#include <map>
using namespace std;

// We need a dictionary to store the word mappings.  The English word will
// be the key and the Spanish word will be the value.

map<string, string> dictionary;

// This function loads the dictionary.

void initializeDictionary() {
    dictionary["blue"] = "azul";
    dictionary["red"] = "rojo";
    dictionary["green"] = "verde";
}

// This function returns the lower case version of a given string.  For
// example, toLowerCase("dOGs") == "dogs".

string toLowerCase(string s) {
    string result;
    for (int i = 0; i < s.length(); i++) {
        result += tolower(s[i]);
    }
    return result;
}

// This function asks the question and writes whether the answer was correct
// or not.

void testUser(string englishWord) {
    string answer;
    cout << "How do you say " << englishWord << " in Spanish?\n";
    cin >> answer;
    cout << "\"" << answer << "\" is ";
    if (toLowerCase(answer) == dictionary[englishWord]) {
        cout << "correct\n";
    } else {
        cout << "is wrong; it's " << dictionary[englishWord] << "\n";
    }
}

// The program just asks a few questions.

int main() {
    initializeDictionary();
    map<string, string>::iterator i;
    for(i = dictionary.begin(); i != dictionary.end(); i++) {
        testUser(i->first);
    }
    return 0;
}

Here is an example of the use of files and streams and command line arguments.

// ---------------------------------------------------------------------------
// average.cpp
//
// This program displays the average of a bunch of floating point numbers.
// If no commandline arguments are given, it prompts the user for a file
// name and averages the floats from that file.  If exactly one commandline
// argument is given, it takes the argument to be a file name and averages
// the floats in that file.  If more than one command line argument is
// given, it assumes they are all floats and displays the average of them.
//
// Files of floats must contain ONLY floats (and whitespace), if any other
// character appears the program will fail with an error message.  When
// averaging command line arguments, each argument must be a legal floating
// point number without trailing whitespace.
//
// The program returns 0 on success, and -1 on failure.
// ---------------------------------------------------------------------------

#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

// This helper returns the average of all the floats in the file named by
// filename.  It assumes the file contains ONLY floats (and whitespace).
// If it finds any other characters it throws an exception.

float averageFromFile(string filename) {

    // Open the file if you can. If you can't, throw a string stating that
    // the file that could not be opened.
    ifstream file;
    file.open(filename.c_str());
    if (!file) {
        throw string("File '" + filename + "' not found");
    }

    // Accumulate the sum and count the values throughout the file.
    // Immediately close the file and throw if any problems are found.
    float sum = 0.0;
    int numberOfValues = 0;
    float value;
    while (true) {
        file >> value;                      // get the next float value
        if (file.bad()) {                   // always check for trashed files
            file.close();
            throw string("Corrupted file");
        }
        if (file.fail()) {                  // couldn't read a float
            if (file.eof()) {               // if it's because you're at eof
                break;                      // ... that's actually fine
            } else {                        // otherwise
                file.close();               // ... you found junk
                throw string("Garbage in file");
            }
        }
        sum += value;
        numberOfValues++;
    }

    // Clean up and return the answer if you can compute it, otherwise throw.
    file.close();
    if (numberOfValues == 0) {
        throw string("Nothing to average");
    }
    return sum / numberOfValues;
}

// This helper computes the average of the C-strings in positions 1 .. argc-1
// in the array argv interpreted as floats.  Note we cannot use atof because
// that pathetic operation returns 0.0 if a particular C-string is an illegal
// float.  So how could we tell the difference between a valid string like
// "0.0" and an illegal one without doing the conversion ourselves?

float averageOfCommandLineArguments(int argc, char** argv) {
    float sum = 0.0;
    for (int i = 1; i < argc; i++) {
        istringstream argument(argv[i]);
        float value;
        argument >> value;

        // Ensure it is a legal float without trailing characters.
        if (argument.bad() || argument.fail() || !argument.eof()) {
            throw string("Non-float commandline argument ") + argv[i];
        }
        sum += value;
    }
    return sum / (argc - 1);
}

// The main function interprets the command line, defers to one of the helper
// routines to compute the average, then displays either the average or any
// error messages that were thrown.

int main(int argc, char** argv) {
    float average;
    try {
        if (argc == 1) {
            string filename;
            cout << "What file do you want to average the values in? ";
            cin >> filename;
            average = averageFromFile(filename);
        } else if (argc == 2) {
            average = averageFromFile(argv[1]);
        } else {
            average = averageOfCommandLineArguments(argc, argv);
        }
        cout << "The average is " << average << '\n';
        return 0;
    } catch (string s) {
        cout << "Error: " << s << "\n";
        return -1;
    }
}

C++ Program Structure

Good to know:

Declarations and Definitions

A declaration introduces a name into a program. A declaration can be definitional or non-definitional. A definition actually creates a thing. There are only five kinds of non-definitional declarations:

DeclarationExample
Incomplete Classclass Dog;
External Variableextern int x;
Function Prototypedouble mag(Vector v);
Typedeftypedef struct {double x; double y;} point;
Static Data Memberclass C {static int x;};

Every top level name must have exactly one definition — so you can't duplicate a global variable definition, and you can't declare something extern in one file and forget to supply it in another file when the program is linked.

Identifiers have:

Types

Built-in: bool
char, unsigned char, signed char, wchar_t,
short int, signed short int, unsigned short int,
int, signed int, unsigned int,
long int, signed long int, unsinged long int,
float, double, long double
Pseudo type: void
Type formers: enum
* (pointer)
[ ] (array)
& (reference - not really a new type)
( ) (function)
union
struct, class

Here is an example of a program that uses most of the types

// ---------------------------------------------------------------------------
// typedemo.cpp
//
// Examples of the built-in types of C++ and how to define new ones.
// ---------------------------------------------------------------------------

#include <iostream>
#include <string>
#include <cmath>
using namespace std;

// For most applications the only built-in types you normally use are
// bool, char, int, and double.  The signed and unsigned versions of char
// and int are really only used in low-level code; float is pretty much
// useless; long double is used in high-precision scientific applications.
// wchar_t probably should be used more often.

void builtinTypeDemo() {
    bool old = true;
    bool young = (1 == 2) || false;

    char c = '%';
    char d('#');                        // alternate initialization syntax

    int x;                              // shows initial values are optional
    int y = 50 + int(c);                // shows a type conversion

    double z = 353.13411;

    if (old && !young) {
        cout << d << double(y) + z << '\n';
    }
}

// Here is a function that is useful in debugging.  If the condition you
// supply is true, nothing happens, but if it is false it displays a
// message then exits the program.  Note that if you don't supply your
// own message it uses "oops".

void verify(bool condition, string errorMessage = "oops") {
    if (!condition) {
        cout << errorMessage << '\n';
        exit(1);
    }
}

// This function illustrates the simplest uses of pointers.

void simplePointerDemo() {
    int x = 10;
    int* p;

    p = &x;                             // now p "points to" x
    *p = 7;                             // this changes x
    verify(x == 7);

    p = NULL;                           // using *p now will probably crash!

    p = new int;                        // allocate memory at run time
    *p = 12;
    int* q = p;                         // p and q point to same place
    *q = 10;
    verify(p == q && *p == 10);

    q = new int(20);
    verify(p != q && *q == 20);
    *q = 10;
    verify(p != q && *p == *q);

    delete p;
    delete q;
}

// This function illustrates references.  When you make a reference to an
// object you can manipulate the object through the reference.  In fact,
// it is like having more than one name for the object.

void simpleReferenceDemo() {
    int x;
    int& y = x;
    y = 100;
    verify(x == 100);
}

// Here is an illustration of how pointers and references affect the ways
// arguments are related to their parameters.

void parameterDemo1(int a, int* b, int& c) {
    a = 101;
    *b = 102;
    c = 103;
}

void callDemo1() {
    int x = 1, y = 2, z = 3;
    parameterDemo1(x, &y, z);
    verify(x == 1 && y == 102 && z == 103);
}

// Illustration of arrays.  WARNING: Arrays in C++ are low-level objects and
// should only be used in low-level code, such as that which implements
// useful data structures or which maps to the hardware.  They are tricky
// and do not behave like the other types.  But you have to learn about them
// because everyone uses them and you have to read other people's code,
// and even write low-level code yourself.
//
// IN GENERAL USE vector AND valarray FROM THE STANDARD LIBRARY INSTEAD
// OF THE "BUILT-IN" ARRAYS.

int a[10];                              // an array of 10 ints
typedef int triple[3];                  // a type declaration, NOT an object
triple t1, t2;                          // two objects
triple center = {0, 0, 0};              // this syntax only for initialization

// Illustration of arrays as parameters.  BEWARE!  Something tricky occurs
// in C++ !!

void move(triple t, int dx, int dy, int dz) {
    t[0] += dx;
    t[1] += dy;
    t[2] += dz;
}

void callDemo2() {
    triple c = {1, 2, 3};
    move(c, 100, 100, 100);
    verify(c[0] == 101 && c[1] == 102 && c[2] == 103);
    // Why did c change even though the first parameter to move was not a
    // reference parameter?  Because the array name is actually treated
    // as a pointer to the first element of the array!

    triple d = {c[0], c[1], c[2]};
    verify(c != d);                             // why??
    // d = c; CANNOT BE WRITTEN IN C++! YOU CANNOT ASSIGN ARRAYS!

    for (int i = 0; i < 10; i++) a[i] = 0;
    move(a, 10, 10, 10);                        // not a error!
    move(&a[7], 10, 10, 10);                    // not an error!
    cout << "[ ";
    for (int j = 0; j < 10; j++) cout << a[j] << ' ';
    cout << "]\n";
}

// Arrays of characters, and hence pointers to characters, are pretty common
// in low-level code.  In regular programs, stick to the type string from
// the standard library.  CHARACTER ARRAYS AND CHARACTER POINTERS ARE
// ERROR-PRONE!  Be very careful with them.  C++ requires discipline.

void charArrayDemo() {
    char s1[3] = {'d', 'o', 'g'};
    char s2[4] = "dog";
    verify(s2[0] == 'd' && s2[2] == 'g' && s2[3] == char(0));
    char* s3 = "aardvark";
    verify(s3[2] == 'r');
    s1[2] = 't';
}

// Here are some declarations of enumeration types.

enum Direction {NORTH, SOUTH, EAST, WEST};
enum Level {LOW, MEDIUM, URGENT};

// Functions as objects.  Actually it is "pointers to functions" that we deal
// with, but in C++ you can use the function name in for a pointer to that
// function.

int square(int x) {
    return x * x;
}

typedef int fun(int);

int twice(fun f, int x) {
    return f(f(x));
}

void twiceDemo() {
    cout << twice(square, 2) << '\n';
}

// Simple classes.

const double PI = acos(-1.0);

class Circle {
private:
    double radius;
public:
    Circle() {radius = 1.0;}
    Circle(double r) {radius = r;}
    double getRadius() {return radius;}
    double circumference() {return 2.0 * PI * radius;}
    double area() {return PI * radius * radius;}
    void expand(double factor) {radius *= factor;}
};

void circleDemo() {
    Circle c1;
    Circle c2(5.0);
    Circle c3(c1);
    Circle c4 = c2;                     // initialization
    c1 = c2;                            // assignment
    c1.expand(3.0);
    verify(c1.getRadius() == 15.0);
    verify(c2.getRadius() == 5.0);
    cout << c3.area() << '\n';
}

// main() just calls all the demos.

int main() {
    builtinTypeDemo();
    simplePointerDemo();
    simpleReferenceDemo();
    callDemo1();
    callDemo2();
    charArrayDemo();
    twiceDemo();
    circleDemo();
    return 0;
}

Statements

Classes

C++ began life as a language called "C with Classes," so not surprisingly there are a variety of concepts related to classes in C++. Here are a few:

Standard Library Modules

There are 50 modules in the standard library:

algorithm, bitset, cassert, cctype, cerrno, cfloat, ciso646, climits, clocale, cmath, complex, csetjmp, csignal, cstdarg, cstddef, cstdio, cstdlib, cstring, ctime, cwchar, cwctype, deque, exception, fstream, functional, iomanip, ios, iosfwd, iostream, istream, iterator, limits, list, locale, map, memory, new, numeric, ostream, queue, set, sstream, stack, stdexcept, streambuf, string, typeinfo, utility, valarray, vector

Differences from C

Many people learn C before C++ and write C++ like C with a couple enhancements. This is very unfortunate and one of the biggest reasons real-world C++ programs are so poor. Learn C++ first. Properly written C++ looks completely foreign to C programmers. Some differences are that C++:

A great document describing the differences between C90, C99 and C++ is http://david.tribble.com/text/cdiffs.htm.