Lecture 17
Templates
Class parametrized by a type
Used to store other things - like new classes
Put implementation in
.h
files
Consider the linked list implementation:
Limitations of this implementation: Can only hold a list of ints How to improve this implementation: If we want to store other types, must use void *
pointers or templates
Consider the linked list implementation with TEMPLATES:
Allows us to declare that the following is parameterized over the type
T
. So inside the part that follows,T
refers to a typename that we can specify in our client code.The
Node<SOME_TYPE_GOES_HERE>
is a template specializationName of the class is still Node, but we still need to write when it is ambiguous what type T might be
Allows us to have a linked list of whatever type we want.
When encountering a template specialization like Node, compiler creates in memory a new class identical to Node with T replaced with the actual type (similar to the preprocessor, but more powerful, and also typesafe)
i.e. Compilier specializes templates at the source code level, before compilation.
Note: We can even do Node< Node<int> >
or even more nested structures. But, note that Node<Node<int>>
doesn't compile because >>
is a right shift operator, so we need to add a space.
In-Class Template Example:
The Standard Template Library (STL)
Has a large # of useful templates eg. dynamic-length arrays: ```vector
Vector Operations
Indexing
the
v[i]
is the ith element of vGoing out of bounds is undefined behaviour
Use
v.at(i)
Checked version of
v[i]
So what happens when you go out of bounds?
What should happen? (Raise an Exception)
Problem:
vector's code can detect the error, but doesn't know what to do about it
client can respond, but can't detect the error
C Solution: functions return a status code or set the global variable errno
leads to awkward programming
encourages programmers to ignore error-checks
C++ Solution: when error arises, the function raises an exception What happens? By default, execution stops
Exception (Try and Catch)
But we can write handlers to catch extensions to deal with out of bounds. .vector<T>::at
throws std::out_of_range
when it fails. We can handle is as follows:
Stack Unwinding
Consider the following:
What happens in the above code?
main calls h
h calls g
g calls f
f raises out_of_range
Control goes back through the call chain (unwinds the stack) until handler (the catch) is found, in this case, back to main (where main handles exception)
If there is matching handler in the entire call chain => program terminates
A handler might do part of the recovery job, i.e. execute some corrective code and throw another exception.
Throw
throw; vs. throw s {...};
(from code above):
.throw s {...};
s may be a subtype of SomeErrorType
throws a new exception of type SomeErrorType
.throw;
actual type of s is retained
In the end, it's better to just say throw;
Catching all Errors
A handler can act as a catch-all
Note: You can throw anything you want - Don't always have to throw objects
Never let a destuctor throw an exception
Why not?
Program will abort immediately (defined behaviour)
If you want a destructor to throw, then tag it with
noexcept(false)
But - if a destructor is running during stack unwinding, while dealing with another exception and it throws, you now have 2 active unhandled exceptions and the program will abort immediately
Last updated