COMP2006 Lecture 14 Exceptions and RAII
COMP2006 Lecture 14 Exceptions and RAII
C++ Programming
Lecture 14
Dr Chao Chen
1
This lecture: exceptions and RAII
How could we report errors?
1. Return an error value from function
– Remember to check return value on each call
– Must have a valid ‘error’ return value
– How do we propagate the error? (return again?)
10
Catching Sub1 class objects
struct Base int test1()
{ {
virtual void temp() {} try
}; {
Sub1 s1;
struct Sub1 : public Base throw s1;
{ }
void temp() {}
}; catch ( Sub1& b )
A { cout << "Sub1" << endl; }
struct Sub2 : public Base
{ catch ( Base& b )
void temp() {}
B { cout << “Base" << endl; }
};
catch ( Sub2& b )
C { cout << "Sub2" << endl; }
Sub-class Sub1 object is thrown
catch ( ... )
Which catch clause will be used? D { cout << "Other" << endl; }
} 11
Answer
• A
• Check catches in order:
– It is a Sub1
12
Catching Sub2 class objects
struct Base int test1()
{ {
virtual void temp() {} try
}; {
Sub2 s2;
struct Sub1 : public Base throw s2;
{ }
void temp() {}
}; catch ( Sub1& b )
A { cout << "Sub1" << endl; }
struct Sub2 : public Base
{ catch ( Base& b )
void temp() {}
B { cout << “Base" << endl; }
};
catch ( Sub2& b )
C { cout << "Sub2" << endl; }
Sub-class Sub2 object is thrown
catch ( ... )
Which catch clause will be used? D { cout << "Other" << endl; }
} 13
Answer
• B
• Check catches in order:
– It is not a Sub1
– It is a Base (Sub2 objects are Base objects)
• Note: The order here matters
– It gets caught by the Base catch before it gets
to the Sub2 catch
– The compiler may give you a warning here
about the sub-class type exception being
caught by the base class catch
– gcc / g++ will
14
Catching Sub2 class pointer
struct Base int test1()
{ {
virtual void temp() {} try
}; {
Sub2* ps2 = new Sub2;
struct Sub1 : public Base throw ps2;
{ }
void temp() {}
}; catch ( Sub1& b )
A { cout << "Sub1" << endl; }
struct Sub2 : public Base
{ catch ( Base& b )
void temp() {}
B { cout << “Base" << endl; }
};
catch ( Sub2& b )
C { cout << "Sub2" << endl; }
Sub-class Sub2 object is thrown
catch ( ... )
Which catch clause will be used? D { cout << "Other" << endl; }
} 15
Answer
• D
• Check catches in order:
– It is not a Sub1
– It is not a Base
– It is not a Sub2
– … catches all exceptions
• Pointers are not objects
• Objects are not pointers
• Note: References and objects will match
– & Just says whether a copy is made or not
16
Exceptions summary
and final comments
17
Aside: exceptions and throw()
• throw() at the end of the function declaration
limits the exceptions which can be thrown
– It is optional
– In Java, throws <types> is mandatory
• If specified, then all exception types which can
be thrown by the function must be specified
– Throwing a different type will terminate the program
• Examples:
void MyFunction(int i) throw();
• Function will not throw exceptions (noexcept).
void MyFunction(int i) throw(int);
• Function will only throw ints as exceptions
void MyFunction(int i) throw(...);
• Function could throw ANY exception 18
Other Exception Comments
• The destructor is guaranteed to be called for a stack
object when the stack frame is destroyed
– It is the only function which we can guarantee will be called when
an exception occurs
– Make sure that you free any heap memory first
19
Exceptions Advice
• Try to catch (and handle) an exception as close as
possible to the place it was generated
• Do not catch an exception if you cannot do
something with it (leave it to your caller)
• If you throw exceptions, prefer to throw standard
class library exceptions, or sub-classes of these
– Choose meaningful exception (e.g. std::invalid_argument)
• My suggestion – and ONLY a suggestion:
– There is a risk involved in using exceptions – i.e. less
control over the flow of control, like an implicit return,
so, use exceptions only for exceptional circumstances
• Note: C++ has no ‘finally’ clause – it uses RAII… 20
RAII
21
Any issue with this function?
void foo()
{ Allocate memory
int* iarray = new int[100];
for (int i=0;i<100;i++)
{
iarray[i] = rand(); Set each element to a random value
if ( (iarray[i]%5) == 0 )
{ End function if random
cout << "end " << i; number gives specific values
return;
}
25
When a function ends…
• Remember back to the discussion of the stack…
– When a function ends, its stack frame is removed
– ALL stack objects (local variables) are destroyed
• Destructors are called for each
– This applies even if the function is ended due to an exception!
• RAII takes advantage of this
– My opinion (only mine?): may be better named in this case:
“Resource Release On Object Destruction” (RROOD?)
• On initialisation, get the resource
• On destruction, release the resource
• The main idea of RAII:
– Create stack object to ‘wrap’ the thing you need to release
– When stack object is destroyed, the thing gets released (e.g. file closed)
26
Simplest(?) file ‘wrapper’ class
class Wrapper void version2a()
{ {
public: Wrapper w;
FILE* pFile; w.pFile = fopen(
"out2a.txt", "w" );
// No constructor -
default created fprintf( w.pFile,
"Output text" );
// Key part is the
destructor! // Do something which could
~Wrapper() // throw exception
{ throw 1;
fclose(pFile);
} // Never gets to fclose
}; // but we don't care now
// No need for:
// fclose(w.pFile);
} 27
Class definition Code to use it
A better wrapper class
class MyFile // Is file open?
{ bool isopen()
FILE* pFile; { return pFile != NULL;
}
Object can say whether file is open
public:
// Constructor // Close file if open
MyFile( void close()
const char* szFileName, {
const char* szType = "r" ) if ( pFile != NULL )
: pFile(NULL) fclose( pFile );
{ pFile = NULL;
pFile = fopen( } And allow you to close it
szFileName, szType );
} // Destructor!!!
~MyFile()
// Conversion operator!!! {
operator FILE*() close();
{ return pFile; } } 28
Use object anywhere a FILE* is needed };
Using the wrapper
void version2b() Reminder:
{ class MyFile
// FILE* f = fopen("out2.txt","w"); {
MyFile file( "out2.txt", "w" ); public:
MyFile( ... )
Create object – attempts to open file
{ /* fopen() */ }
30
Wrapping pointers/arrays : int*
class Deleter class ArrayDeleter
{ {
public: public:
int* pOb; // wrapped ptr int* pArray;
Deleter<MyClass> del(pOb);
34