## Chapter 17: Advanced C++ Topics

## Chapter Goals

- To learn about operator overloading
- To learn how to automatically manage dynamic memory
- To understand nested classes
- To define and use name spaces
- To be able to implement templates
- To become familiar with exception handling

## Operator Overloading

- Many classes use operators such as `++`, `*` and `==.`
- Giving a new meaning to an operator is called operator overloading.
- You can overload an operator by defining a function whose name is operator followed by the operator symbol.
- For example, you can define the difference between two `Time` objects as the number of seconds between them.

  >     int operator-{Time a, Time b)
  >     {
  >        return a.seconds_from(b);
  >     }

- Now you can use the `-` operator instead of calling `seconds_from`.

  >     Time now;
  >     Time morning(9, 0, 0);
  >     long seconds_elapsed = now - morning;

## Operator Overloading (Syntax 17.1 : Overloading Operator Definition)

Syntax 17.1 : Overloading Operator Definition
    return_type operatoroperator_symbol(parameters)
    {
       statements
    }
Example: |
    int operator-(Time a, Time b)
    {
       return a.seconds_from(b)
    }
Purpose: | Supply the implementation of an overloaded operator.

## Operator Overloading

- Does it make sense to add two times?

  >     some_return_type operator+(Time a, Time b);

- A `Time` objects represent a point in time, not a duration: what does 3 P.M. + 3 P.M. mean?
- It does make sense to add a number of seconds to a `Time` object, resulting in a new `Time` object.

  >     Time operator+(Time a, int sec)
  >     {
  >        Time r = a;
  >        r.add_seconds(sec);
  >        return r;
  >     }

## Overloading Operators

- A commonly overloaded operator is the `==` operator, to compare two values.

  >     bool operator==(Time a, Time b)
  >     {
  >        return a.seconds_from(b) == 0;
  >     }

- For completeness, it is a good idea to also define a `!=` operator.

  >     bool operator!=(Time a, Time b)
  >     {
  >        return a.seconds_from(b) != 0;
  >     }

- You may also find it useful to define a `<` operator.

  >     bool operator<(Time a, Time b)
  >     {
  >        return a.seconds_from(b) < 0;
  >     }

## Overloading Operators

- For many classes, you want to print the object with the familiar `<<` notation.
- The output operator takes a parameter of type `ostream&` (because printing modifies the stream) and the object to be printed.

  >     ostream& operator<<(ostream& out, Time a)
  >     {
  >        out << a.get_hours() << ":";
  >        if (a.get_minutes() < 10) out << "0";
  >        out << a.get_minutes() << ":";
  >        if (a.get_seconds() < 10) out << "0";
  >        out << a.get_seconds();
  >        return out;
  >     }

- The`<<` operator returns the out stream to enable chaining of the `<<` operator.

  >     cout << now << "\n";

  really means

  >     (cout << not) << "\n";

  that is

  >     operator(cout, now) << "\n";

- The call to `operator(cout, now)` returns `cout`, then `cout << "\n"` prints a new line.

## Operator Overloading

- You can also overload the input operator to read in other types of objects.

  >     istream & operator>>(istream& in, Time& a)
  >     {
  >        int hours;
  >        int minutes;
  >        int seconds;
  >        in >> hours >> minutes >> seconds;
  >        a = Time(hours, minutes, seconds);
  >        return in;
  >     }

- Note that the `>>` operator returns the input stream just like the `<<` operator.
- Unlike the `<<` operator, the `>>` operator must have a parameter of `Time&`.
- It is easy to go overboard overloading operators! Using inappropriate operators can make programs more difficult to read.
  - Does it make sense to overload `*`, `/` or `%` for Time objects?

## Operator Overloading (`overload.cpp`)

## Operator Overloading

- There are actually two forms of the `++` and `--` operators.
  - A prefix form:

    >     ++x;

  - A postfix form:

    >     x++;

- Recall that `++x` evaluates to `x` after the increment, and `x++` evaluates to `x` before the increment.

  >     int i = 0;
  >     int j = 0;
  >     vector<double> s(10);
  >     double a = s[i++]; /* a is s[0], i is 1 */
  >     double b = s[++j]; /* b is s[1], j is 1 */

- In order for the compiler to distinguish between the two versions, the operators must have two different parameter lists.

  >     void operator++(Time& a) /* prefix operator */
  >     . . .

  >     void operator++(Time& a, int dummy) /* postfix operator */

- The `int dummy` parameter is not used inside the function, it merely serves to differentiate the two `operator++` functions.

## Operator Overloading

- Some operators must access the internals of the class and must be member functions.
- Member functions use the implicit parameter as the left operand.
- A non member operator:

  >     bool operator==(Iterator a, Iterator b)

- The same operator as a member function:

  >     bool Iterator::operator==(Iterator b) const

- If the operator is unary, then the member function has no explicit parameter

  >     string Iterator::operator*() const

## Operator Overloading

- Actual implementation of the operators is somewhat anticlimactic.

  >     string Iterator::operator*() const
  >     {
  >        assert(position != NULL);
  >        return position->data;
  >     }

  >     bool Iterator::operator==(Iterator b) const
  >     {
  >        return position == b.position;
  >     }

- Note the parameter in the postfix version of the `++` operator.

  >     void Iterator::operator++(int dummy) const
  >     {
  >        assert(position != NULL);
  >        return = position->next;
  >     }

- Also note that the `!=` operator uses the `==` operator.

  >     bool Iterator::operator!=(Iterator b) const
  >     {
  >        return !(*this == b); // calls operator ==
  >     }

## Automatic Memory Management

- For this discussion, we will use a modification of the Department class that was introduced earlier.

  >     class Department
  >     {
  >        ...
  >     private:
  >        string name;
  >        Employee* receptionist;
  >     };

  >     Department::Department(string n, Employee)
  >     {
  >        name = n;
  >        receptionist = new Employee(e.get_name(), e.get_salary());
  >     }

  >     /* second constructor */

  >     Department::Department(string n)
  >     {
  >        name = n;
  >        receptionist = NULL;
  >     }

## Automatic Memory Management

- A `Department` object contains a pointer to an `Employee` object.
- When the `Department` object is destroyed (goes out of scope, for example) the `Employee` object is leaked.
- In C++, you can define a destructor, a function that is called when an object is about to go out of scope.
- The destructor for the `Department` class should delete the receptionist pointer.

  >     Department::~Department()
  >     {
  >        delete receptionist;
  >     }

- Note that calling delete on a `NULL` pointer is safe, so you don't need a special case for that situation.

## Automatic Memory Management (Syntax 17.2 : Destructor Definition)

Syntax 17.2 : Destructor Definition
    Class_name::~Class_name()
    {
       statements
    }
Example: |
    Department::~Department()
    {
       delete receptionist;
    }
Purpose: | Supply the implementation of a destructor that is invoked whenever and object goes out of scope.

## Automatic Memory Management

- The destructor is automatically invoked (not called directly).

  >     {
  >        Department dept;
  >        ...
  >     } // dept.~Department() automatically invoked here

  >     ...
  >     Department* p = new Department(...);
  >     ...
  >     delete p; // p->~Department() automatically invoked here

- A class can only have one destructor with no parameters.
- You should always supply a destructor when some amount of clean up is required when an object goes out of scope.
  - Recycling dynamic memory (as shown here).

- Close a file.
- Relinquish some other resource.

## Automatic Memory Management

- Consider the following declarations.

  >     Department qc("Qualitiy Control", Employee("Tester, Tina", 50000));
  >     Department dept("Shipping", Employee("Hacker, Harry", 35000));

- Suppose we assign one department to the other.

  >     dept = qc;

- This assignment causes a memory leak! (See following slide).
- When one object goes out of scope (and the other doesn't) the `Employee` is destroyed, causing a dangling pointer.

## Automatic Memory Management

- The remedy is to overload the `operator=` to make the assignment safe by deleting the only `Employee` object, and making a copy of the new `Employee` object.

  >     Department& Department::operator=(const Department& b)
  >     {
  >        if (this != & b)
  >        {
  >           name = b.name;
  >           delete receptionist;
  >           if (b.receptionist == NULL)
  >              receptionist == NULL;
  >           else
  >              receptionist = new Employee(b.receptionist->get_name(),
  >                 b.receptionist->get_salary());
  >         }
  >         return *this;
  >     }

- Unlike most other operators, the `operator=` must be a member function.
- Special care must be taken to avoid a destructive "self-assignment."
- A return by reference is required to handle chaining of assignments, such as.

  >     z = y = x;

- You should always overload the = operator if your class has data fields that are pointers, and a simple copy of objects leads to dangerous shared pointers.

## Automatic Memory Management

- The purpose of `operator=` is to set an existing object equal to another object.
- Use of `=` for construction is not always appropriate.
- Example: Here, the pointer `dept.receptionist` is set to a random value. The operator will try to delete the receptionist causing an error.

  >     Department dept = qc;

- The copy constructor defines how to construct an object of a class as a copy of another object of the same class.

  >     Department dept(qc)

- If you don't define a copy constructor, then the compiler provides a version that simply copies the corresponding data fields of the existing object.
- This version of the copy constructor is still inappropriate, leading to the same kind of errors as the default version of the assignment operator.

## Automatic Memory Management

- Here is a valid copy constructor for the `Department` class.

  >     Department::Department(const Department& b)
  >     {
  >        name = b.name;
  >        if (b.receptionist == NULL)
  >           receptionist = NULL;
  >        else
  >           receptionist = new Employee(b.receptionist->get_name(),
  >              b.receptionist->get_salary());
  >     }

- The parameter must be `const` reference because pass by value invokes the copy constructor!

## Automatic Memory Management

- The assignment operator, copy constructor, and destructor are collectively called the "big three."
- You must implement all three for any class that manages heap memory.
- Each function has the following logic.
  - Destructor:
    Free all dynamic memory that the object manages.
  - Copy Constructor:
    Initialize the object as a copy of the explicit parameter object.
  - Assignment Operator:
    Check whether `this == &b`. If so do nothing.
    Free the dynamic memory of the object that is no longer needed.
    Set the object as a copy of the explicit parameter object.
    Return `*this`.

## Automatic Memory Management (`department.cpp`)

## Memory Management for Linked Lists

- When a linked list (Chapter 16) is no longer needed, the nodes should be deleted.
- To automate this task you define a destructor.

  >     List::~List()
  >     {
  >        free();
  >     }

  >     void List::free()
  >     {
  >        while(begin() != end())
  >           erase(begin());
  >     }

- The free function is kept separate from the destructor because its implementation is also needed for the assignment operator.

  >     List& List::operator=(const List& b)
  >     {
  >        if (this != &b)
  >        {
  >           free();
  >           copy(b);
  >        }
  >        return *this;
  >     }

## Memory Management for Linked Lists

- As part of the "Big 3", we supply a copy constructor for the list class at well.

  >     List::List(const List& b)
  >     {
  >        first = NULL;
  >        last = NULL;
  >        copy(b);
  >     }

- These memory management functions will be included in the `List` implementation that follows.

## Templates

- A class template is a mechanism that allows us to create classes whose data fields have arbitrary type.
- We have already seen an example: the vector template.

  >     vector<int> v_i;
  >     vector<double> v_d;
  >     vector<Employee> v_e;

- To define a template, denote the arbitrary type with a type parameter as show in syntax 17.3 (next slide), and add the line `template<typename T>` before the class definition.

  >     template<typename T>
  >     class Pair
  >     {
  >     public:
  >        Pair(T a, T b);
  >        T get_first() const;
  >        T get_second() const;
  >     private:
  >        T first;
  >        T second;
  >     };

## Templates (Syntax 17.3 Template Class Definition)

Syntax 17.3 : Template Class Definition
    template<typename type_variable>
    class class_name
    {
       features
    };
Example: |
    template<typename T>
    class Pair
    {
    public:
       Pair(T a, T b);
       T get_first() const;
       T get_second() const;
    private:
       T first;
       T second;
    };
Purpose: | Define a class template with a type parameter.

## Templates

- When you implement each member function, you must make it a template as indicated in syntax 17.4.

  >     template<typename T>
  >     Pair<T>::Pair(T a, T b)
  >     {
  >        first = a;
  >        second = b;
  >     }

  >     template<typename T>
  >     T Pair<T>::get_first() const
  >     {
  >        return first;
  >     }

  >     template<typename T>
  >     T Pair<T>::get_second() const
  >     {
  >        return second;
  >     }

- Each function is turned into a separate template.
- Each function name is prefixed by the "`Pair<T>::`" qualifier.
- Note the use of the type variable `T` to represent a type.

## Templates (Syntax 17.4 Template Member Function Definition)

Syntax 17.4 : Template Member Function Definition
    template<typename type_variable>
    return_type class_name<type_variable>::function_name(parameters) constopt
    {
       statements
    }
Example: |
    template<typename T>
    T Pair<T>::get_first() const
    {
       return first;
    }
Purpose: | Supply the implementation of a member function for a class template.

## Templates

- A template version of the `List` class presented in chapter 16 follows.

  >     template<typename T>
  >     class List
  >     {
  >     public:
  >        List();
  >        void push_back(T s);
  >        void insert(Iterator<T> pos, T s);
  >        void erase(Iterator<T> pos);
  >        Iterator<T> begin();
  >        Iterator<T> end();
  >     private:
  >        Node<T>* first;
  >        Node<T>* last;
  >     }

- Both the `Node` and `Iterator` classes need to be made templates as well.
- Each member function must also be made into a template.

  >     template<typename T>
  >     Iterator<T> List<T>::begin()
  >     {
  >        Iterator<T> iter;
  >        iter.position = first;
  >        iter.position = last;
  >        return iter;
  >     }

## Templates (`list.cpp`)

## Nested Classes and Name Spaces

- In the standard library, the `iterator` class is nested inside the `list` class.

  >     list<string>::iterator pos = staff.begin();

- The reason is so several different implementations (for vectors, lists, maps, and sets) can all share the same name (iterator), but have different implementations.

  >     vector<double>::iterator p = a.begin();
  >     list<string>::iterator q = b.begin();

- To nest a class inside another, first declare the nested class inside the outer class.

  >     class List
  >     {
  >        ...
  >        class Iterator;
  >        ...
  >     };

## Nested Classes and Name Spaces (Syntax 17.5 : Nested Class Declaration)

Syntax 17.5 : Nested Class Declaration
    class Outer_class_name
    {
       ...
       class Nested_class_name
       {
          ...
       };
       ...
    }
Example: |
    class List
    {
       ...
       class Iterator
       {
          ...
       };
    };
Purpose: | Define a class whose scope is contained in the scope of another class.

## Nested Classes and Name Spaces

- The definition of the member functions requires references to both the outer and inner classes.

  >     List::Iterator::Iterator()
  >     {
  >        ...
  >     }

  >     string List::Iterator::get() const
  >     {
  >        ...
  >     }

- Note that the name of constructor is just `Iterator`.
- In general, use nested classes for just one reason: to place the name of a class inside the scope of another class.

## Nested Classes and Name Spaces

- Name spaces are also used to avoid naming conflicts.
- By using name spaces, it becomes possible to use classes or functions with the same name in the same program.

  >     std::map /* standard library template map */
  >     acme::map /* another class called map */

- To add classes, functions, or variables to a name space, surround their declarations with a `namespace` block.

  >     namespace acme
  >     {
  >        class map
  >        {
  >           ...
  >        };

  >        void draw(map m);
  >     }

- Namespaces are open - you can add as many items to a namespace as you like by starting another `namespace` block.

  >     namespace acme
  >     {
  >        class maze
  >        {
  >           ...
  >        }
  >     };

## Nested Classes and Name Spaces (Syntax 17.6 : Name Space Definition)

Syntax 17.6 : Name Space Definition
    namespace name_space_name
    {
       feature1
       feature2
       ...
       featuren
    };
Example: |
    namespace ACME_Software_San_Jose_CA_US
    {
       class map
       {
          ...
       };
    }
Purpose: | Include a class, function or variable in a name space.

## Nested Classes and Name Spaces

- Instead of using a prefix qualifier, if there are no conflicts we can just start a program with a `using` declaration.
- This allows us to use `cout` instead of `std::cout`.
- Since you create name spaces to avoid name clashes, you want to name them in a unambiguous (usually long) way, such as `ACME_Software_San_Jose_CA_US`.
- Unfortunately, it would be painful to constantly type

  >     ACME_Software_San_Jose_CA_US::map

- You can define a short alias for a long name space.

  >     namespace acme = ACME_Software_San_Jose_CA_US

## Nested Classes and Name Spaces (Syntax 17.7 : Name Space Alias)

Syntax 17.7 : Name Space Alias
    namespace alias_name = name_space_name;
Example: |
    namespace acme = ACME_Software_San_Jose_CA_US;
Purpose: | Introduce a short alias for the long name of a name space.

## Exception Handling

- How do you handle programmer errors when using a function or class?
  - Don't check the error condition and executing the code anyway, possibly causing a problem.

    >     double future_value(double initial_balance, double p, int n)
    >     {
    >        return initial_balance * pow(1 + p / 100, n);
    >     }

  - Check the error condition and doing nothing if it occurs.

    >     double future_value(double initial_balance, double p, int n)
    >     {
    >        if (p < 0 || n < 0) return 0;
    >        return initial_balance * pow(1 + p / 100, n);
    >     }

  - Check the error condition and alert the caller if it occurs.

    >     double future_value(double initial_balance, double p, int n)
    >     {
    >        assert(p >= 0 && n >= 0);
    >        return initial_balance * pow(1 + p / 100, n);
    >     }

## Exception Handling

- Problems:
  - If you don't check for the error, your program may be unreliable.
  - If you return a fake value, the program continues using the value. No one knows what the error was, only that something went wrong.
  - Many real pieces of software can't be halted, such as one that controls a medical device.
- C++ has a mechanism called exception handling. When a function detects an error, it can signal (throw an exception) a condition to some other part of the program whose job it is to deal with errors.

  >     double future_value(double initial_balance, double p, int n)
  >     {
  >        if (p < 0 || n < 0)
  >        {
  >           logic_error description("illegal future_value parameter");
  >           throw description;
  >        }
  >        return initial_balance * pow(1 + p / 100, n);
  >     }

## Exception Handling (Syntax 17.8 : Throwing an Exception)

Syntax 17.8 : Throwing an Exception
    throw expression;
Example: |
    throw logic_error("illegal future_value parameter");
Purpose: | Abandon this function and throw a value to an exception handler.

## Exception Handling

- Here, `logic_error` is a standard exception class that is declared in the `<stdexcept>` header.
- The keyword `throw` indicates that the function exits immediately, but the function does not return to the caller. Instead, the program searches for a handler that specifies how to handle the logic error.
- You supply an exception handler with the `try` statement.

  >     try
  >     {
  >        code
  >     }
  >     catch (logic_error& e)
  >     {
  >        handler
  >     }

## Exception Handling (Syntax 17.9 : Try Block)

Syntax 17.9 : Try Block
    try
    {
       statements
    }
    catch (type_name1 variable_name1)
    {
       statements
    }
    catch (type_name2 variable_name2)
    {
       statements
    }
    ...
    catch (type_namen variable_namen)
    {
       statements
    }
Example: |
    try
    {
       List staff = read_list();
       process_list(staff);
    }
    catch(logic_error& e)
    {
       cout << "Processing error " << e.what() << "\n";
    }
Purpose: | Provide one or more handlers for types of exceptions that may be thrown when executing a block of statements.

## Exception Handling

- If any of the functions in the `try` clause throw a `logic_error`, or call another function that throws such an exception, then the code in the catch clause executes immediately.

  >     while (more)
  >     {
  >        try
  >        {
  >           code
  >        }
  >        catch (logic_error& e)
  >        {
  >           cout << "A logic error has occurred "
  >              << e.what() << "\n"
  >              << "Retry? (y/n)";
  >           string input;
  >           getline(cin, input);
  >           if (input == "n" more = false;
  >        }
  >     }

- The `what` member function of the `locic_error` class returns the string that was passed to the constructor of the error object in the throw statement.

## Exception Handling

- It's a good idea to use inheritance to define your own exception types.

  >     class FutureValueError : public logic_error
  >     {

  >     public:
  >        FutureValueError(const char reason[]);
  >     };

  >     FutureValueError::FutureValueError(const char reason[])
  >        : logic_error(reason){}

- Here, the `future_value` function can now throw a `FutureValueError` object.

  >     if (p < 0 || n < 0)
  >        throw FutureValueError("illegal parameter");

- Because `FutureValueError` is a `logic_error`, you can still catch it with `catch (logic_error& e)`.
- You can even catch other types of errors. Note that the exception handling mechanism matches the handlers top to button, and executes the first matching handler.

  >     try
  >     {
  >        code
  >     }
  >     catch (FutureValueError& e)
  >     {
  >        handler1
  >     }
  >     catch (logic_error& e)
  >     {
  >        handler2
  >     }

## Exception Handling

- One common use of exception handling is in functions that read input.
- Example: suppose that no price or score is given?

  >     bool Product::read(fstream& fs)
  >     {
  >        getline(fs, name);
  >        if (name == "") return false; // end of file
  >        fs >> price >> score;
  >        if (fs.fail())
  >           throw runtime_error("Error while reading product");
  >        string remainder;
  >        getline(fs, remainder);
  >        return true;
  >     }

- When a logic error occurs, it never makes sense to retry the same operation, but a runtime error may have some chance of going away when the operation is attempted again.
- Note that this function distinguishes between the expected end of file (returning false), and an unexpected problem (which throws an exception).

## Exception Handling

- Here we see the hierarchy of standard exception types in C++.

## Exception Handling

- Consider the following function:

  >     void process_products(fstream& fs)
  >     {
  >        list<Product> products;
  >        bool more = true;
  >        while (more)
  >        {
  >           Product p;
  >           if (p.read(fs)) products.push_back(p);
  >           else more = false;
  >        }
  >        do something with products
  >     }

- If the `read` function throws an exception, the `process_products` functions halts, and the exception handling mechanism searches for an appropriate handler.
- The C++ exception handling mechanism invokes all destructors of stack objects before it abandons a function.
- In our example, the list object is destroyed, along with all `Product`s in the list.

## Exception Handling

- This automatic invocation of destructors only applies to objects.
- Consider the following code.

  >     Product* p = new Product();
  >     if (p->read())
  >     {
  >        ...
  >     }
  >     delete p; // never executes if read throws an exception

- If an exception occurs in the `read` function, `p` receives no special attention because it is not an object.
- A memory leak occurs.

## Exception Handling

- The best remedy is to make sure that all allocated memory is deleted in a destructor.
- If a local pointer variable i unavoidable, you can use the following construct:

  >     Product* p = NULL;
  >     try
  >     {
  >        p = new Product();
  >        if (p->read())
  >        {
  >           ...
  >        }
  >        delete p;
  >     }
  >     catch(...)
  >     {
  >        delete p;
  >        throw;
  >     }

- The clause `catch(...)` matches any exception.
- The handle contains the local cleanup, followed by the `throw` statement without an exception object.
- This rethrows the exception so that the correct handler can process it.

## Exception Handling

- If an exception is thrown and no `catch` clause exists to catch it, then the program terminates with an error message.
- Because of potential dangers in throwing exceptions, a function can declare that it only throws exceptions of a certain type, or no exceptions at all.
- You can use that knowledge to make sure that it is safe to call certain functions, or to know which kinds of exceptions you program needs to catch.

  >     void process_products(fstream& fs)
  >        throw (UnexpectedEndOfFile, bad_alloc)

- A function signature can optionally be followed by the keyword `throw` and a parenthesized, comma-separated list of exception types; for example:
- To denote that a function throws no exceptions, use an empty exception list.

  >     void process_products(fstream& fs)
  >        throw ()

- A function without a `throw` specification is allowed to throw any exceptions.

## Exception Handling (Syntax 17.10 : Exception Specification)

Syntax 17.10 : Exception Specification
    return_type function_name(parameters)
       throw (type_name1, type_name2, ..., type_namen)
Example: |
    void process_products(fstrream& fs)
       throw(UnexpectedEndOfFile, bad_alloc)
Purpose: | List the types of all exceptions that a function can throw.