Summary

We will first start off with a small test to check your inheritance skills after session 4. Afterwards, "safe programming" is the main topic of today's lab session: error handling through the use of exceptions. We'll also cover a small addition to operator overloading: user-defined typecast operators. Also covered: a utility library for parsing command-line arguments and some interesting debugging tools to help improve your (project) C++ code.

Test your inheritance skills

Look at following source code files. Try to figure out what is going on and what will be printed. Check your answer by running the actual code.

Test 1:

#include <iostream>

class Base {
public:
    void print() { std::cout << "Base!" << std::endl; }
};


class Derived : public Base {
public:
    void print() { std::cout << "Derived!" << std::endl; }
};

int main()
{
    Base b;
    Derived d;
    b = d;
    b.print();

    Base* b2 = new Derived();
    b2->print();
}

Test 2:

#include <iostream>

class Base {
public:
    Base() { std::cout << "Base constructor!" << std::endl; }
    Base(int b) : x(b) { std::cout << "One argument Base constructor!" << std::endl; }
    virtual void print() { std::cout << "Base!" << std::endl; }
private:
    int x;
};


class Derived : public Base {
public:
    Derived() { std::cout << "Derived constructor!" << std::endl; }
    Derived(int d) : x(d) { std::cout << "One argument Derived constructor!" << std::endl; }
    virtual void print() { std::cout << "Derived!" << std::endl; }
private:
    int x;
};

int main()
{
    Base* b2 = new Derived(5);
    b2->print();
}

Test 3:

#include <iostream>

class Base {
public:
    Base() { std::cout << "Base constructor!" << std::endl; }
    virtual ~Base() { std::cout << "Base destructor!" << std::endl; }
};


class Derived : public Base {
public:
    Derived() { std::cout << "Derived constructor!" << std::endl; }
    ~Derived() { std::cout << "Derived destructor!" << std::endl; }
};

int main()
{
    Base* b = new Derived();
    delete b;

    Derived d2;
    Base b2(d2);
}

Be sure that you really understand what is going with each test. If you have questions concerning the output, do not hesitate to ask!

Exception handling

Important C++ has a well-structured mechanism to report and handle errors and unforeseen events: exceptions. (unforeseen or exceptional in this context means: "some part of the system couldn't do what it was asked to do")

An exception is an object of some class representing an exceptional event. It might (and often does) contain additional information about the cause of the event; for instance a string containing a more detailed error message. Since exceptions are instances of classes, they can be grouped in hierarchies of exceptions. This allows for a hierarchical error handling: specific errors (think: derived classes) can be handled closer to where the error occurred; for instance deep in the internals of some library.

More general errors (think: base classes) can be handled higher up in the hierarchy; for instance in client code that uses the library.

Basic usage

The code that could throw an exception is wrapped in a try block. Whenever an exception is thrown, the program's control flow immediately jumps to the catch blocks that directly follow the try block. The catch blocks are considered in order; the one that first matches the thrown exception's type is executed. After handling the exception in the catch block, the program resumes "normal" execution right after the catch blocks.

Play around with the example below to see how it works:

try {
    // Code that might throw an exception, for example:
    throw MyException();
} catch (MyException& e) {
    // Handle a 'MyException'
    cout << "MyException: don't worry, be happy!" << endl;
} catch (YourException& e) {
    // Handle a 'YourException'
    cout << "YourException: this really is the end of it all!" << endl;
} catch (...) {
    // catch-all: handle every other exception that did not match
    // any of the above catch clauses
    cout << "Something happened but I don't know what..." << endl;
}

cout << "Just kidding, carry on!" << endl;

The semantics of catching an exception is the same as a function accepting an argument: initialization. If you want to prevent slicing, you'll have to catch exceptions by pointer or by reference, as is done in the example above. You can also use const to prevent accidental changes to exceptions in handlers.

Nesting

Obviously try-catch blocks can be nested; for example:

try {
    // stuff to try
    try {
        // other stuff to try
    } catch (std::exception &e) {
        // solve problem
    } catch (...) {
        // solve another problem
    }
} catch (...) {
    // whatever
}

Watch out you don't end up reproducing if-then-else blocks with a mess of nested or linear exception handlers: read C++FAQ on exceptions.

Rethrow

A previously thrown exception can be re-thrown for further error handling:

try {
    // whatever
} catch (std::exception &e) {
    // handle the error partially
    throw;
}

Note that the original exception is re-thrown! (I.e.: no slicing etc...)

Constructors & destructors

As you might have guessed, special care needs to be taken when exceptions are used during an object's construction and destruction. When an exception is thrown in a constructor, the object is not fully constructed. The destructor (that might otherwise free the previously allocated resources) of the "half-baked" object will not be called, which leaves the object in a potentially half-initialized state with dangling resources. A good rule of thumb is that every member object must manage its own destruction. This is discussed in the C++ FAQ's section on selfcleaning members.

I highly recommend you to read cppreference's RAII: Resource Acquisition Is Initialization programming idiom now. This idiom also goes by the more intuitive name "Scope-Bound Resource Management".

Constructors (don't forget the copy constructor!) allow a way of enclosing their initializer lists in a try block as well. Consider this hypothetical example:

Sprite.h:

/**
    The Sprite class represents an image that can be drawn on the screen.
*/
class Sprite {
    public:
        Sprite(std::string file_name);
        ~Sprite();
        void setImage(Image img);
        void draw(Surface s, Coord2d xy);

    private:
        Image m_img;
};

Sprite.cpp:

#include "Sprite.h"

Sprite::Sprite(std::string file_name)
try : m_img(file_name) {
      // constructor
} catch (exception& e) {
    cout << "Error loading sprite image: " << e.what() << << endl;
}

Sprite::~Sprite() {
    // We don't need to clean up anything. Image's destructor will releaase
    // its own resources. Cfr.: RAII.
}

void Sprite::setImage(Image img) {
    m_img = img;
}

void Sprite::draw(Surface s, Coord2d xy) {
    // Draw sprite's image on the surface s at position xy
    ...
}

Whenever a Sprite is being constructed requesting an image file that cannot be accessed for whatever reason, Images's constructor throws an exception that will be caught by Sprite's constructor. The situation can be resolved later by loading the proper image manually with Sprite::setImage.

As a general rule, destructors must not throw exceptions!. More specifically, if a destructor throws an exception, it must be handled internally within the destructor. It may not leave the destructor's body! See: C++ FAQ 17.9.

Of course, you can try this just for fun:

class A {
    public:
        ~A() {
            throw 1;
        }
};

int main() {
    try {
        A a;
        throw 1;
    } catch (...) {
        cout << "Will you print me, dear C++?" << endl;
    }
    return 0;
}

Notice I'm throwing an int variable, which is perfectly fine in C++. You can throw whatever you want, as long as it can be copied. This doesn't mean that you should do this though. Generally it is considered best-practise to only throw exceptions that are derived from std::exception.

Remarks

Except in very special cases (have a look at the last paragraph of in this C++ FAQ thread) the memory allocated by the new operator will be freed properly when the constructor of the class whose object is being allocated throws an exception. For example, in cases like:

Foo* f = new Foo;

or

Foo* f = new Foo[10];

The correct use of exceptions is a difficult subject and attracts a lot of extreme opinions. Read some articles to broaden your perspective. People even dedicate whole lectures to exception safe coding in C++. Have a look; you'll probably learn something new.

Standard library

The C++'s standard library has a wide variety of different exceptions to handle all the possible error scenarios. See here for an overview. Notice that all exceptions in the standard library are derived from the std::exception base class (#include <exception> to make it work).

The std::exception class has a (virtual) what() method to retrieve a string with more information about the error. In the case of the base class this error message is an empty string. More specific error messages are provided by subclasses of std::exception. Try this:

try {
    vector<int> v = {2, 3, 5, 7};
    v.at(4) = 11;
} catch (exception& e) {
    cout << e.what() << endl;
}

Notice v.at(4) is used instead of the more familiar v[4]. The difference is that at() does range checking and throws an std::out_of_range exception if you're trying to access elements outside the container's range. On the other hand, [] does no range checking whatsoever and possibly results in a less gracious "Segmentation fault" in the above example.

Of course, you can derive your own exceptions using std::exception as a base class. If you just want a simple exception class with a string as a "reason" and no custom hierarchy of errors you can use std::runtime_error (You'll need to #include <stdexcept>). It's really very convenient!

try {
    ...
    throw runtime_error("Computer says NO!");
    ...
} catch (runtime_error& e) {
    cout << "An error occurred: " << e.what() << endl;
}

Look at cppreference.com for a short explanation about which exception fits which situation: save yourself some work and check this list first before implementing your own exception class! Beware that deriving directly from std::exception is perfectly fine, but has the disadvantage that you do not have a constructor that accepts a std::string which is returned by the what() call. Plus, you should also check the list to see if there is a more specific exception from which you should derive.

Operator overloading (bis)

There's a useful set of operators that we haven't covered yet which can be overloaded as well: the type-cast operators. Recall the Integer wrapper exercise / example from session 2. There is, for instance, no way of doing:

Integer a = 4;
int b = a;

You'll get:

error: cannot convert 'Integer' to 'int' in initialization

To make the above statement possible you need to overload the type-cast operator Integer::operator int(). The proper way of doing this is:

Integer.h:

class Integer {
    ...
    operator int() const;
    ...
};

Integer.cpp:

Integer::operator int() const {
    return this->m_val;
}

Commandline arguments

In some cases it's extremely useful to provide your program with command line arguments.
That's where the mysterious int argc, char* argv[] arguments of the main function are for: argc contains the number of (whitespace-separated) arguments passed (argument count), argv is an array of null terminated strings that contains these arguments (argument values). Note that main is a very special function, being the only one that has optional parameters and return value (the compiler will return 0 if nothing is specified), so you might not always encounter it in this form. Try this:

#include <iostream>

int main(int argc, char* argv[]) {
    for (int i = 0; i < argc; ++i) {
        std::cout << argv[i] << std::endl;
    }

    return 0;
}

Compile & run:

g++ -o arguments main.cpp
./arguments --foo "a" bar -baz=3

You should see:

./arguments
--foo
a
bar
-baz=3

Note that the name of the program is an argument as well! This can be used in nifty ways. An example of this is BusyBox, a collection of small versions of the most common UNIX utilities bundled in one executable. Depending on the link that's used to call the executable, different routines will be called. This saves a lot of disk space in systems where storage is limited and reduces the need for libraries!

If you've got time to waste you can start writing your own parser that can deal with all kinds of possible situations to interpret the arguments in argv. There are quicker ways to deal with that issue though.

TCLAP

TCLAP stands for "Templatized C++ Command Line Parser". It's a small, useful header-only library to handle command line arguments elegantly. Try this short example:

#include <iostream>
#include <tclap/CmdLine.h>

using namespace std;

int main(int argc, char* argv[]) {
    try {
        // Object that manages the command line parsing
        TCLAP::CmdLine cmd("My program", ' ', "0.1");

        // Define the arguments
        TCLAP::ValueArg<int> n_arg("n", "num_whatever",
                                   "Number of whatevers I need for whatever else",
                                   false, 0, "integer");

        TCLAP::ValueArg<string> input_arg("i", "in", "Input file name", true,
                                          "", "file path");

        TCLAP::SwitchArg verbose_arg("v", "verbose", "Verbose mode");

        // Add arguments to the parser
        cmd.add(n_arg);
        cmd.add(input_arg);
        cmd.add(verbose_arg);

        // Parse arguments
        cmd.parse(argc, argv);

        // Use the parsed arguments
        cout << "n = " << n_arg.getValue() << endl;
        cout << "i = " << input_arg.getValue() << endl;
        cout << "v = " << verbose_arg.getValue() << endl;

    } catch (TCLAP::ArgException &e) {
        cout << "error: " << e.error() << " for arg " << e.argId() << endl;
    }

    return 0;
}

If you run the program with the --help argument (or forget to use the correct arguments), you'll see that automatically you also have a nice help page! Have a look at the TCLAP manual or these TCLAP slides for more details about its usage. It's really convenient! You can install it on Ubuntu using sudo apt install libtclap-dev, or directly download the latest source.

boost::program_options

Another alternative is to use argument parsing facilities from boost: Boost.Program_options. Have a look at the documentation for more details. There's a tutorial as well.

Debugging tools

Important Two popular tools to improve your C++ programs are GDB and Valgrind. They assist you in, among other things, debugging C++ code and check for memory leaks (in say, a game programming project).

  • GDB: allows you to see what is going on inside another program while it executes -- or what another program was doing at the moment it crashed. You can find a useful tutorials on GDB here and on cprogramming.com (but there are lots and lots of others).
  • Valgrind: the Valgrind distribution contains several tools that can check for memory errors (memory leaks, segmentation faults, invalid pointers, uninitialized variables, ...) or thread errors (and several other types of errors). Check their Quick Start documentation (they also have a complete user manual for more detailed information) or a cprogramming.com tutorial (this is C oriented, but of course can also be applied to C++).

Try to debug following code using GDB and fix it:

#include <iostream>

using namespace std;

long factorial(int n) {
    long result(1);
    while (n--) {
        result *= n;
    }
    return result;
}

int main() {
    int n(0);
    cin >> n;
    long val = factorial(n);
    cout << val;
    return 0;
}

Is something wrong with this piece of code? Find out with Valgrind!

#include <iostream>
#include <vector>

using namespace std;

class Foo {
public:
    Foo(int instance) : m_instance(instance) {};
    ~Foo() {};
    void print() { cout << "Foo instance " << m_instance << endl; }
private:
    int m_instance;
};

int main() {
    vector<Foo*> fooVec (10);
    for (int i = 0; i < 10; i++) {
        fooVec[i] = new Foo(i);
    }

    for (int i = 9; i >= 0; i--) {
        fooVec[i]->print();
    }

    fooVec.erase (fooVec.begin(), fooVec.end());

    return 0;
}

Exercises

Divide by zero

Go to the assignment: https://classroom.github.com/a/9vZDabfq

Abstract Pizza Factory

Go to the assignment: https://classroom.github.com/a/xtwkowhc

Delivery Observer

Go to the assignment: https://classroom.github.com/a/ISxOWwJf