Software Design (C++) Software Design (C++) 3. Resource management and 3. Resource management and
exception safety exception safety
(idioms and technicalities) (idioms and technicalities)
Juha Vihavainen Juha Vihavainen University of Helsinki University of Helsinki
Preview Preview
More on error handling and exceptions More on error handling and exceptions
checking array indices (again)
checking array indices (again) -- why not? why not?
handling complicated error situations handling complicated error situations the RAII principle
the RAII principle Smart pointers
Smart pointers and resource management and resource management Implementing
Implementing exception safety exception safety built
built--in language support in language support
programming techniques and idioms programming techniques and idioms
Juha Vihavainen / University of Helsinki
Juha Vihavainen / University of Helsinki 22
25.11.2014 25.11.2014
Index
Index checking checking
//
// an almost real an almost real vector vector of of double doubless::
# include <stdexcept>
# include <stdexcept> // // . . . . . . // // get get std::out_of_range std::out_of_range class
class V Vector { ector { // // my custom container. . . my custom container. . . public:
public: // // . . . . . .
double& operator [] (
double& operator [] (int int n); n); // // unsafe but max efficiency unsafe but max efficiency double& at (int n);
double& at (int n); // // index index is is checked checked //
// . . . . . . };
};
//
// possible implementations possible implementations ((as as allowed allowed by the C++ standard by the C++ standard)) double& Vector::operator [] (int n) { return elem [n]; } double& Vector::operator [] (int n) { return elem [n]; } double& Vector::at (int n) {
double& Vector::at (int n) {
if (n < 0 || sz <= n) throw std::out_of_range ("at: invalid index");
if (n < 0 || sz <= n) throw std::out_of_range ("at: invalid index");
return elem [n];
return elem [n];
}}
Juha Vihavainen / University of Helsinki
Juha Vihavainen / University of Helsinki 33
25.11.2014 25.11.2014
Reminder: Why not index checking?
Reminder: Why not index checking?
Checking costs
Checking costs in speed and code size in speed and code size not much, don’t worry
not much, don’t worry few
few real real--world projects need world projects need to worry to worry But
But some some projects may really require optimal projects may really require optimal performance performance tthink hink huge (e.g., Google) huge (e.g., Google) and very tiny and very tiny (e.g., cell phone) (e.g., cell phone) The
The standard must standard must serve everybody (is still competing with C) serve everybody (is still competing with C)
""you can’t build optimal on top of checked you can’t build optimal on top of checked""
""you you can build checked on top can build checked on top of optimal of optimal""
Some projects cannot even be allowed
Some projects cannot even be allowed to use exceptions to use exceptions o
old ld projects with pre projects with pre--exception parts exception parts h
high igh reliability reliability, hard real , hard real--time time code (think code (think airplanes) airplanes) The C part
The C part of C++ can't give security guarantees anyway! of C++ can't give security guarantees anyway!
Juha Vihavainen / University of Helsinki
Juha Vihavainen / University of Helsinki 44
25.11.2014 25.11.2014
Range
Range checking: an example checking: an example
The following small program tests out the
The following small program tests out the at at operation: operation:
int main () { . . . int main () { . . .
try { try {
std::vector <int> v;
std::vector <int> v;
for (int i = 0; i < n; ++i) v.push_back (i);
for (int i = 0; i < n; ++i) v.push_back (i);
for
for ((int int i = 0 i = 0;; i <= v.size (); i <= v.size (); ++ ++ii)) // // oops oops, but , but checked checked std::cout
std::cout << << " "v [" v [" << << ii << << "] == " "] == " << << v.at v.at (i (i)) << ' << '\\n n'; ';
}}
catch (std::out_of_range const&) {
catch (std::out_of_range const&) { // // we’ll we’ll get get here here std::cout
std::cout << "out of range << "out of range error error"; ";
return
return 1 1;; // // just give up here.. just give up here..
}}
catch ( ... ) {
catch ( ... ) { // // something else something else }}
}}
5 5 25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
Exception
Exception handling and cleanup handling and cleanup -- the hard way the hard way
(works OK but
(works OK but not recommended not recommended)) //
// sometimes sometimes we need to do we need to do a a cleanup job and give a chance to continue cleanup job and give a chance to continue Vector
Vector * * someFunction () someFunction () // // returns a filled returns a filled Vector Vector {{
Vector
Vector * * pV pV = = new new Vector; Vector; // // dynamic allocation dynamic allocation ((bad style bad style -- //
// => => someone someone must must deallocate deallocate)) try
try {{
fillVector
fillVector ((* *pV); pV); // // could fail and could fail and throw throw ((we assume here we assume here)) return pV;
return pV; // // all’s well; return all’s well; return the filled vector the filled vector }} // // ""local local--recovery recovery--rethrow rethrow"" idiom idiom::
catch
catch ( … ) { ( … ) { // // catch any exception what catch any exception what--so so--ever ever delete pV;
delete pV; // // do do our our own local own local cleanup cleanup throw
throw;; // // re re--throw throw to to allow allow the the caller to deal caller to deal }}
}}
Juha Vihavainen / University of Helsinki
Juha Vihavainen / University of Helsinki 66
25.11.2014 25.11.2014
Exception
Exception handling with destructors handling with destructors
(simpler and more
(simpler and more structured idiom) structured idiom) //
// using scoped using scoped ((local local)) variables makes the cleanup automatic variables makes the cleanup automatic void someOtherFunction ()
void someOtherFunction () // // uses a filled uses a filled vector vector {{
Vector v
Vector v;; // vector // vector handles deallocation handles deallocation fillVector
fillVector (v); (v); // // pass as a reference pass as a reference //
// use v here . . . use v here . . .
}} // v // v is automatically destructed is automatically destructed the compiler
the compiler--generated destructor call at end of block works generated destructor call at end of block works like an implicit
like an implicit finally finally clause clause if
if you feel that you need you feel that you need aa try try--catch catch block block:: think again think again you
you might be able to might be able to do do much better without much better without it it
Juha Vihavainen / University of Helsinki
Juha Vihavainen / University of Helsinki 77
25.11.2014 25.11.2014
RAII:
RAII: Resource Resource Acquisition Acquisition Is Initialization Is Initialization
std::vector std::vector
acquires memory for elements in its constructor (or later) acquires memory for elements in its constructor (or later) manages it (changing size, controlling access, etc.) manages it (changing size, controlling access, etc.) gives back (releases) the memory in its destructor gives back (releases) the memory in its destructor
This is a special case of the general resource management strategy This is a special case of the general resource management strategy called RAII (for in
called RAII (for in--depth discussion, see [ depth discussion, see [Stroustrup Stroustrup,, 2014, Ch. 19.5]) 2014, Ch. 19.5]) also called “scoped resource management”
also called “scoped resource management”
use it wherever you can use it wherever you can
simpler and cheaper than anything else simpler and cheaper than anything else
interacts elegantly with error handling using exceptions interacts elegantly with error handling using exceptions examples of
examples of resources resources: memory, file handles, sockets, I/O : memory, file handles, sockets, I/O bindings (
bindings (iostreams iostreams handle those using RAII), locks, widgets, handle those using RAII), locks, widgets, threads
threads
Juha Vihavainen / University of Helsinki
Juha Vihavainen / University of Helsinki 88
25.11.2014 25.11.2014
Using raw pointers considered harmful Using raw pointers considered harmful
What
What if if you you can't can't help help creating creating dynamic dynamic objects objects (an API (an API requires requires)) void f (
void f (int int x) { x) { X
X * * p = new X (x); p = new X (x); // // allocate an allocate an X X ((bad style bad style)) // . . .
// . . . use use p p- -> >f () f () . . . . . . delete p;
delete p; // // might be never executed might be never executed!!
}}
Can
Can use use aa smart smart pointer pointer for for ownership ownership and and life life--cycle cycle control control
void f ( void f (int int x) { x) {
std::
std::shared_ptr shared_ptr <X> p (new X (x)); // <X> p (new X (x)); // immediately hand in the immediately hand in the X X // . . .
// . . . use use p p - -> > f () f () -- provides overloaded pointer operations provides overloaded pointer operations } // p
} // p is local is local:: so at exit, its destructor deletes the owned so at exit, its destructor deletes the owned X X
Juha Vihavainen / University of Helsinki
Juha Vihavainen / University of Helsinki 99
like a pointer but also keeps a reference count (here = 1)
25.11.2014 25.11.2014
or:
or: . . = X::CreateX (...) . . = X::CreateX (...)
Sample smart pointer:
Sample smart pointer: shared_ptr shared_ptr
the C++11 standard provides
the C++11 standard provides std::shared_ptr std::shared_ptr (plus others..) (plus others..) heavily uses overloading, to create a pointer
heavily uses overloading, to create a pointer--like object like object template <typename T> class shared_ptr { //
template <typename T> class shared_ptr { // a general solution for a general solution for public:
public: // // reference counting reference counting T
T * * operator operator- -> > () const { return get (); } () const { return get (); } T& operator
T& operator * * () const { return () const { return * *get (); } get (); } void reset ();
void reset (); . . . . . . // // make empty make empty ((nullptr nullptr)) long use_count () const;
long use_count () const; // // how many owners how many owners??
bool unique () const;
bool unique () const; // // one owner only one owner only??
operator bool () const;
operator bool () const; // // whether whether get() get() != != nullptr nullptr T
T * * get () const; get () const; // // return raw pointer return raw pointer . . .
. . . // // but cannot release ! but cannot release !
Juha Vihavainen / University of Helsinki
Juha Vihavainen / University of Helsinki 1010 25.11.2014
25.11.2014
overloads conversion
overloads conversion
11 11
Safe exception handling: background Safe exception handling: background
O
Originally riginally, exceptions and , exceptions and errors errors were very poorly understood were very poorly understood aspect of C
aspect of C++; also, expection ++; also, expection safety issues and requirements safety issues and requirements were seen and added to the C
were seen and added to the C++89/03 ++89/03 standard at standard at last moments last moments [Stroustrup, 2000]
[Stroustrup, 2000] App App.. E: E: Standard Standard--Library Exception Library Exception Safety Safety Article on
Article on Exception Safety Exception Safety http://www.stroustrup.com/except.pdf http://www.stroustrup.com/except.pdf Boost article:
Boost article: Exception Exception--Safety in Generic Components Safety in Generic Components http://www.boost.org/community/exception_safety.html http://www.boost.org/community/exception_safety.html T
Two wo concerns: concerns:
1.
1. class invariants must be maintained class invariants must be maintained -- or restored or restored 2.
2. no no resources may be leaked resources may be leaked
including: memory space, opened files, locks, including: memory space, opened files, locks, connections,
connections, or or any other any other shared system shared system resources resources
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
12 12
Invariants revisited Invariants revisited
U
Use se defensive programming and self defensive programming and self--checking objects checking objects A
A class invariant class invariant is an assertion that holds before and after any is an assertion that holds before and after any operation manipulating an object
operation manipulating an object P
Preconditions reconditions tests tests "external" "external" failures which the unit cannot failures which the unit cannot handle itself
handle itself: must : must throw an exception throw an exception if (!
if (! precondition precondition )) throw
throw AnException ("diagnostics AnException ("diagnostics"); // "); // back to back to the caller the caller IInvariants nvariants and and (testable) (testable) postconditions postconditions check check internal states internal states that don't make sense to outsiders,
that don't make sense to outsiders, and most often and most often indicate a indicate a bug in code
bug in code => => use asserts to eliminate them use asserts to eliminate them assert ( isInValidInternalState
assert ( isInValidInternalState_); _); // // aborts aborts if not if not Often,
Often, we don't know the we don't know the actual reason actual reason of a failure: perhaps a of a failure: perhaps a programming error or some external factor
programming error or some external factor
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
13 13
Invariants revisited (cont Invariants revisited (cont.) .)
The
The programming programming--by by--contract contract separates responsibilities but separates responsibilities but actual production software is not so that clear cut and clean actual production software is not so that clear cut and clean Preconditions provide
Preconditions provide a pragmatic trade a pragmatic trade--off off what to check what to check, at , at the boundary of a
the boundary of a unit (class/function/method) unit (class/function/method) N
Note ote that the C++ standard library uses the same strategy that the C++ standard library uses the same strategy (provides checks
(provides checks for selected operations for selected operations that that may throw) may throw) The
The fundamental problem fundamental problem with exception safety with exception safety
an exception thrown from some component or function may an exception thrown from some component or function may interrupt the algorithm and leave the state of the calculation interrupt the algorithm and leave the state of the calculation (objects) in
(objects) in some bad or some bad or indeterminate indeterminate state state if the
if the class invariant does not class invariant does not hold, hold, the object cannot be even the object cannot be even destructed
destructed without causing undefined behavior without causing undefined behavior
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
14 14
Many
Many sources of exceptions sources of exceptions
U
User ser--supplied supplied and system functions, such as allocator functions, and system functions, such as allocator functions, can throw exceptions (from [
can throw exceptions (from [Stroustrup, 2000]) Stroustrup, 2000])
void fun (std::vector <X>& v
void fun (std::vector <X>& v,, X const& x) X const& x) {{
//
// sample exceptions sample exceptions::
v [2] = x;
v [2] = x; // X // X's 's assignment may throw assignment may throw v.push_back (x);
v.push_back (x); // vector' // vector'ss allocator may throw allocator may throw std
std::vector <X> u = v; ::vector <X> u = v; // X // X's 's copy ctor may throw copy ctor may throw .. . . . .
}} // // u u is destructed is destructed here; here; X X's dtor 's dtor should not should not throw throw!!
Also, IO operations may throw (if so configured) Also, IO operations may throw (if so configured)
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
15 15
Levels of exception safety Levels of exception safety
1.
1. Basic Guarantee Basic Guarantee: no leaks, and : no leaks, and maintains class invariant. maintains class invariant.
2.
2. Strong Guarantee Strong Guarantee: succeeds, or leaves state unchanged. : succeeds, or leaves state unchanged.
3.
3. Nofail Guarantee Nofail Guarantee: doesn't fail : doesn't fail (throw) in (throw) in any circumstances. any circumstances.
the last one (
the last one (Nofail Nofail) is often needed to implement the former ) is often needed to implement the former ones;
ones; e.g e.g., an assignment of a primitive value ., an assignment of a primitive value (say, a pointer (say, a pointer)) cannot fail
cannot fail -- but anything with allocations may fail! but anything with allocations may fail!
the
the strong guarantee strong guarantee for for some some complicated update may complicated update may require a "roll
require a "roll--back" mechanism back" mechanism (sometimes too expensive (sometimes too expensive))
""maintaining class invariant maintaining class invariant"" means means the object is in
the object is in some some valid state (but not necessarily in the valid state (but not necessarily in the one we would
one we would ideally like ideally like it to be) it to be) but it can be at least released and
but it can be at least released and destructed destructed
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
16 16
Levels of exception safety
Levels of exception safety ((cont.) cont.)
e.g.,
e.g., std:: std::vector < vector <T>::push_back T>::push_back is designed to give the is designed to give the strong guarantee:
strong guarantee: a new element is a new element is added or added or no no change change Additionally:
Additionally:
4.
4. Exception Neutrality Exception Neutrality: exceptions originating from : exceptions originating from components are always passed through unmodified components are always passed through unmodified
relevant for a container handling and copying its elements, relevant for a container handling and copying its elements, especially when using C++ templates
especially when using C++ templates e.g., standard
e.g., standard vector <T>::push_back vector <T>::push_back also manifests also manifests exception neutrality
exception neutrality: after any internal clean : after any internal clean--up, propagates up, propagates the original exception caused by the copying operation the original exception caused by the copying operation -- that depends on the actual element type (
that depends on the actual element type (T T))
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
17 17
User can call
User can call constructors and destructor constructors and destructor
C
Constructors onstructors & destructors are & destructors are usually usually called by compiler called by compiler X
X * * ptr = new X; ptr = new X; . . . . . . // // reserve reserve memory, then construct memory, then construct X X delete ptr;
delete ptr; // // destruct destruct X X, then , then release its release its memory memory note that if
note that if p p is is nullptr nullptr (zero), (zero), delete delete has no effect has no effect W
When hen necessary, necessary, allocation allocation can be separated from object can be separated from object initialization
initialization with the so with the so--called called placement placement--new new operator operator void
void * * p = ::operator new ( p = ::operator new (sizeof ( sizeof (X)); X)); // // allocates allocates space space ptr =
ptr = new (p) new (p) X; X; // // placement placement--new constructs new constructs X X at at p p S
Similarly imilarly, we , we could could separate destruction & deallocation separate destruction & deallocation ptr
ptr- -> ~ > ~X () X ();; // // destructs destructs the object pointed by the object pointed by ptr ptr ::operator delete (ptr); //
::operator delete (ptr); // delete delete operator operator frees space frees space Since a
Since a destructor is a member function, destructor is a member function, we we can call it explicitly can call it explicitly -- but but then must then must very carefully ensure very carefully ensure that that the compiler the compiler doesn't! doesn't!
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
18 18
Exceptions and
Exceptions and ctors/dtors ctors/dtors
The following built
The following built--in C++ mechanisms enable resource management in C++ mechanisms enable resource management even in case of failures and exceptions
even in case of failures and exceptions 1.
1. An An exception throw causes the unwinding of call stack exception throw causes the unwinding of call stack all objects located
all objects located (in the call stack) between (in the call stack) between the places where the places where the exception is thrown and caught are destroyed, i.e., their the exception is thrown and caught are destroyed, i.e., their destructors are called
destructors are called 2.
2. Suppose Suppose that an exception is thrown inside a constructor, which that an exception is thrown inside a constructor, which has already constructed one or more
has already constructed one or more embedded members (fields) embedded members (fields) the run
the run--time system time system ("compiler") calls ("compiler") calls the destructors of the the destructors of the already constructed members to release resources reserved by already constructed members to release resources reserved by those members
those members 3
3.. A A failed construction in a failed construction in a ""new new T () T ()"" operation operation also always also always releases the space allocated for the
releases the space allocated for the T T object object
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
19 19
Language support
Language support for exception safety for exception safety
C++ language
C++ language rules/implementation rules/implementation ensure that exceptions thrown while ensure that exceptions thrown while construction will be handled
construction will be handled correctly correctly 1.
1. E Either ither the object is the object is fully built fully built (and its (and its invariants OK), or its members invariants OK), or its members become
become automatically destructed (by the compiler/run automatically destructed (by the compiler/run--time system) time system) 2.
2. Also Also new new operations are implemented safely; operations are implemented safely; ""p = p = new T new T;;"" is is compiled
compiled into into something like ( something like (pseudocode pseudocode here): here):
p =
p = allocate space for a T: // allocate space for a T: // may fail & throw but that's OK may fail & throw but that's OK!!
try
try { construct a T at p; { construct a T at p; // // create it here create it here ((placement new placement new)) }} catch ( ... ) catch ( ... ) {{ // // note exception note exception neutrality neutrality
free the reserved space; //
free the reserved space; // release back release back raw memory raw memory rethrow
rethrow the same exception; the same exception;
}}
the T
the T ctor ctor is assumed is assumed to be "safe": to be "safe": no leaks of its no leaks of its own own
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
20 20
Case
Case: how safety mechanisms work : how safety mechanisms work
C
Consider onsider the following C++ class and code the following C++ class and code class A : public B {
class A : public B { public:
public:
A ()
A () {} {} // // implicit implicit ctor ctor calls for calls for x x and and y y X
X x x; Y ; Y y y;; // // two two ((public public)) members.. members..
}; . . .
}; . . . // // using using implicit dtor implicit dtor ~A () ~A (),, here here
A
A * * a = new A; a = new A; .. .. .. // . . . // . . . some other code some other code delete
delete a; a;
T
The he A A constructor constructor may seem empty may seem empty but actually but actually it it contains contains the the construction
construction of the of the B B,, X X, and , and Y Y parts parts of an of an A A object object S
Similarly imilarly A A's compiler 's compiler--generated destructor handles the destruction generated destructor handles the destruction of all these members
of all these members
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
21 21
Case
Case (cont.) (cont.)
A
A * * p = new A; p = new A; . . . . . . // // create create a a dynamic dynamic A A and and use use it it delete
delete p; p; // // later later get get rid rid of of it it The
The above above code code is is implemented implemented by by the the compiler compiler as as follows follows void
void * * p p = ::operator = ::operator new ( new (sizeof sizeof (A)); (A)); // // ((1 1)) allocate allocate // operator
// operator new new throws throws bad_alloc bad_alloc upon upon failure failure ((no problem no problem)) try
try {{ // // we now have space for an we now have space for an A A new (
new (p p)) A; A; }} // // ((2 2)) create an create an A A at at p p ((or fail or fail)) catch
catch ( ... ) { ( ... ) { // // handle whatever failures handle whatever failures ::operator delete (
::operator delete (p p); throw; ); throw;
}}
. . .
. . . // // some other some other code . . . code . . . ((A
((A* * ))p p))- -> > ~A (); ~A (); // // ((2' 2')) release release A A--specific resources specific resources ::operator delete (
::operator delete (p p); ); // // ((1' 1')) release release A A's space 's space ((via via p p))
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
may throw may throw
cannot cannot throw throw
22 22
On implementing
On implementing your own strong your own strong guarantee guarantee
a sketch
a sketch of strong exception guarantee and of strong exception guarantee and roll roll--back back void doOperation (T const&
void doOperation (T const& value value) { ) { try {
try { < < update update the state copying the giving the state copying the giving value value >; >;
//
// e.g e.g., ., a a copy operation may fail & throw copy operation may fail & throw } catch
} catch ( ... ) ( ... ) {{ // // catch catch any exception any exception
<
< restore restore the old state and its the old state and its invariants invariants >; >;
throw;
throw; // // now now rethrow the original rethrow the original }}
}}
exception
exception neutrality: neutrality: any any T T--related related exceptions pass through exceptions pass through strong guarantee may be very tricky or too costly to achieve;
strong guarantee may be very tricky or too costly to achieve;
even STL
even STL does not provide it for does not provide it for all all its operations its operations special C++ idioms support strong guarantee (see later) special C++ idioms support strong guarantee (see later)
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
23 23
Example
Example: exception safe : exception safe constructor for constructor for Vector Vector
Vector::Vector (size_t sz, T const& x)
Vector::Vector (size_t sz, T const& x) { // { // create a vector of create a vector of sz x' sz x'ss rep_= (T
rep_= (T* *)::operator new (sz )::operator new (sz* *sizeof(T sizeof(T)); )); // // new new may fail may fail T
T * * p = rep_; p = rep_; // // element address element address try {
try { // // construct construct sz sz items items for (; p != rep_+ sz; ++p)
for (; p != rep_+ sz; ++p) new (p) T (x new (p) T (x));; // // ctor ctor may fail may fail }} catch catch ( ... ) ( ... ) {{ // // handle handle T T constructor failures constructor failures
while (p
while (p-- -- != rep_) != rep_) // // destroy destroy all constructed items all constructed items p
p- -> >T::~T () T::~T ();; // // call call T T's destructor 's destructor ::operator delete (rep
::operator delete (rep_); _); // // release release memory memory throw;
throw; // // propagate propagate the original exception the original exception }}
size_= capacity_= sz; //
size_= capacity_= sz; // OK: OK: Vector Vector initialized initialized }}
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
STL container operation STL container operation
Exception
Exception--safe copy ctor, and assignment safe copy ctor, and assignment
Similar
Similar implementation implementation must must be be written written for for copy copy construction construction:: if if copy copy of
of one one item item fails fails,, must must destruct destruct the the previously previously copied copied ones ones . . . . Assignment
Assignment operators operators can can often often be be safely safely programmed programmed with with an an existing
existing copy copy constructor constructor and a ( and a (safe safe)) swap swap::
Vector
Vector& & Vector::operator Vector::operator = ( = (Vector Vector const const& & rhs rhs) { ) { Vector
Vector tmp tmp ((rhs rhs); ); // // may may fail fail ((but no but no problem problem)) //
// can can now now do do the the actual actual assignment assignment,, safely safely and and efficiently efficiently swap
swap ((tmp tmp); ); // // does does not not fail fail ((exchanges exchanges fields fields)) return
return * *this this;;
}} // // tmp tmp is is destructed destructed here here ((old old value value)) we
we assume assume that that the the swap swap operation operation doesn't doesn't fail fail (as (as should should be be!) !) the
the same same requirement requirement needed needed for for Vector Vector's 's destructor destructor ((since since tmp tmp becomes
becomes destroyed destroyed at the at the end end of the of the function function before before the the return return))
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki 2424
25 25
Destructors
Destructors should never fail should never fail, i.e., never allow exceptions to escape , i.e., never allow exceptions to escape from them
from them Exercise
Exercise: Write a test program to show : Write a test program to show w
what hat happens if an exception is thrown out from a destructor while happens if an exception is thrown out from a destructor while the system is still propagating another
the system is still propagating another??
propagating
propagating an exception calls an exception calls destructors (within the call stack) destructors (within the call stack) if
if such a destructor lets such a destructor lets a new a new exception escape, exception escape, the run the run--time time system immediately terminates the program
system immediately terminates the program so a destructor
so a destructor should always catch potential exceptions (if any) should always catch potential exceptions (if any) handle and recover from the
handle and recover from the exception exception -- -- or if not possible or if not possible log
log out an error diagnostics and shut down the program out an error diagnostics and shut down the program the
the compiler cannot check compiler cannot check,, so it is the programmer's responsibility so it is the programmer's responsibility
Destructors are critical for exception handling Destructors are critical for exception handling
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
26 26
Need for exception safety Need for exception safety
Reusable library components vs. basic
Reusable library components vs. basic applications applications
different levels of exception safety can be identified and are different levels of exception safety can be identified and are appropriate in different situations
appropriate in different situations
strong guarantee may be too expensive or not worth it:
strong guarantee may be too expensive or not worth it:
not
not all processing or programs can be made or need to be all processing or programs can be made or need to be absolutely "failure
absolutely "failure safe" safe"
For
For example example
an application program is not necessarily meant to be a separate an application program is not necessarily meant to be a separate reusable component (or a part of a library)
reusable component (or a part of a library) when encountering an error, a simple application
when encountering an error, a simple application could report could report errors, decide to end its execution, discard all calculated results, errors, decide to end its execution, discard all calculated results, and require the user to try it again with
and require the user to try it again with better and more better and more valid valid input
input
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki
Think again Think again
if you feel that you need a
if you feel that you need a delete delete in your code: think again in your code: think again prefer
prefer delete deletes in destructors s in destructors if you feel that you need a
if you feel that you need a try try--catch catch block: think again block: think again you might be able to do much better using RAII you might be able to do much better using RAII if you feel that you need a
if you feel that you need a new new operation.. operation..
prefer scope
prefer scope--controlled objects, or objects that are controlled objects, or objects that are embedded "inline" within "owner objects"
embedded "inline" within "owner objects"
use
use pointers pointers only only when when need need to to replace replace aa whole whole object object "in "in place
place" ( " (say say,, replace replace aa buffer buffer with with a new a new longer longer one one)) at
at least least hide hide the the dynamic dynamic allocation/deletion allocation/deletion inside inside function
function members members
aa vector vector may may reallocate reallocate its its buffer buffer but but that that is is hidden hidden
Juha Vihavainen / University of Helsinki
Juha Vihavainen / University of Helsinki 2727 25.11.2014
25.11.2014
28 28
Summary Summary
write exception
write exception--safe class safe class libraries libraries and and reusable reusable components components three different levels of exception safety can be provided:
three different levels of exception safety can be provided: basic basic,, strong
strong, and , and nofail nofail
especially, the destructors require the
especially, the destructors require the nofail nofail guarantee guarantee exception
exception neutrality neutrality needed especially for templates needed especially for templates ((unknown unknown type parameters with
type parameters with their unknown their unknown exceptions) exceptions) play safe to prevent bugs and to
play safe to prevent bugs and to debug debug add
add redundant checks to verify assumptions redundant checks to verify assumptions
always initialize everything (especially pointers) to minimize always initialize everything (especially pointers) to minimize random and unpredictable states
random and unpredictable states remember to clean up resources
remember to clean up resources (usually via destructors) (usually via destructors) instead of raw
instead of raw pointers use pointers use smart pointers smart pointers (C++11) (C++11)
25.11.2014
25.11.2014 Juha Vihavainen / University of HelsinkiJuha Vihavainen / University of Helsinki