• No results found

vector vec double # in # cl in ude <s ude tdexcept> tdexcept> // std::ou std t_of ::ou _range t_of class class V Vector { ector {

N/A
N/A
Protected

Academic year: 2021

Share "vector vec double # in # cl in ude <s ude tdexcept> tdexcept> // std::ou std t_of ::ou _range t_of class class V Vector { ector {"

Copied!
14
0
0

Loading.... (view fulltext now)

Full text

(1)

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

(2)

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

(3)

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

(4)

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

(5)

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

(6)

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

(7)

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

(8)

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

(9)

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

(10)

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

(11)

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

(12)

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

(13)

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

(14)

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

References

Related documents