Lecture 19
This copy initialization calls Book
constructor and then goes filed-by-field copy initializing (the built-in behavior) the same is true for other compiler provided methods.
To write our own operations:
Note:
Even though the object
other
refers is an rvalue
, other
itself is a named parameter of a function (so it has a lifeline) so other
itself is an lvalue
. The <utility>
header includes the function std::move
which forces an lvalue
to be treated as an rvalue
, so we can all the move operations on it.
The behavior we have written for these functions is the same as the compiler-provided specialize as need for classes that require it.
- *Now consider: **
The result is only the Book components assigned so this is partial assignment.
- **So, how do we fix this? ***
Note: Text::operator=
is permitted to return (by reference) a subtype object and still be valid override, BUT the parameter types MUST be the same, or it is not an override, so by βis-aβ principle, if a Book
can be assigned by another Book
, then a Text
can be assigned to a Book
.
Also, if we had something like:
Before the operator=
was virtual, we had partial assignment through base class pointers, if it is virtual, we get this problem of mixed assignment.
Assignment to base class pointers/references doesnβt make sense - we need to know the two objects we are assigning are the same type. This defeats the purpose of working polymorphically.
To prevent this, we disallow assignment through base class pointers by making the assignment operator private or protected in the base class - can stay public and non-virtual in derived classes.
For example, we can do:
- Here, at checks if valid index, but what happens if
i
is not a valid index? - In
C++
, an exception is raised.
Exception Handling
An exception is some value raised by function that ends execution of that function and passes error along to the caller.
For example, vector.at()
can detect that an error has occurred, but does not know what to do with it. vector.at()
specifically raises a std::out_of_range
object when a bad access is made. std::out_of_range
is an exception class from the header \#include <exception>
. So, what do we do about exceptions.
Here, main
will call g, which calls f
, which then calls vector.at()
which raises an exception. By default, the program stops executing.
More specifically, the f()
propogates the error to g()
, which does the same thing, so the error reaches main
. If an error reaches all the way to main
, and it is not handled in main
, the program terminates.
To prevent this, we can catch errors:
If an exception raising code is wrapped in a try/catch
block, it can handle the exception if it has a matching handler.
What if g()
catches the exception, handle some behaviour, and raises another exception?
What if g()
wanted to raise the same error?
Why do we just write throw
instead of throw e
.