Слайд 2Исключения как механизм обработки ошибок
Варианты обработки ошибок без исключений:
Умолчание ошибки
int atoi ( const char * str );
const char * strVal = “abc”;
…
const int val = atoi(strVal); // результат функции 0
2. Возвратить результат операции
bool strToInt(const char * str, int & out);
const char * strVal = “abc”;
…
int val = 0;
strToInt(strVal , val); // Результат операции не проверяется
Слайд 33. Выставить глобальный флаг ошибки
int errorFlag;
#define ERR_NO_ERROR
#define ERR_INCORRECT_INPUT
…
int strToInt(const char *
str)
{
errorFlag = ERR_NO_ERROR;
…
errorFlag = ERR_INCORRECT_INPUT;
return 0;
…
}
int main()
{
…
const int val = strToInt (strVal); // функция вернет 0
If (errorFlag != ERR_NO_ERROR)
{
…
}
…
}
Слайд 4Базовый синтаксис try..catch…throw
try
{
throw T1();
…
throw T2();
…
throw T3();
} catch(T1 t1)
{
…
} catch(T2 t2)
{
…
} catch
(…) // все типы исключений
{
}
Слайд 5class InputError
{
…
};
int strToInt(const char * str) {
…
throw InputError();
…
}
int main() {
…
try
{
….
const int value = strToInt(strValue);
…
} catch(InputError error)
{
std::cout << “Ошибка ввода”<< std::endl;
}
…
}
Слайд 6Исключение – это один из способов передачи управления
int fun2() {
…
fun();
…
}
try
{
fun2();
} catch(std::runtime_error & err)
{
}
Слайд 7Особенности обработчика catch
try
{
throw E();
} catch(H)
{
…
}
Обработчик catch (H) будет
вызван, если:
Если Н того же типа что и Е;
Если Н является открытой базой Е;
Будет выполнено для указателей и ссылок если выполняется [1] или [2];
Слайд 8Особенности обработчика catch
try
{
throw 4;
// выбросили исключение типа int
} catch(int errNum) // попали в обработчик исключения типа int
{
…
}
Обработчик catch (H) будет вызван, если:
Если Н того же типа что и Е
Если Н является открытой базой Е
Будет выполнено для указателей и ссылок если выполняется [1] или [2]
Слайд 9Особенности обработчика catch
try
{
throw std::runtime_error;
} catch(std::exception err)
{
…
}
Обработчик catch (H)
будет вызван, если:
Если Н того же типа что и Е
Если Н является открытой базой Е
Будет выполнено для указателей и ссылок если выполняется [1] или [2]
Слайд 10Перехват всех исключений и повторная генерация
try
{
…
} catch(…) // перехватить все типы
исключений
{
…
throw; //выброcить еще раз то же самое исключение
…
}
Слайд 11Порядок следования catch - секций
try
{
}
catch (int numErr)
{
…
}
catch
(std::exception exc)
{
...
}
catch(std::runtime_error exc)
{
...
}
Слайд 12Порядок следования catch - секций
try
{
throw std::runtime_error (“”);
}
catch(std::runtime_error
exc) // сработает
{
}
catch (std::exception exc)
{
}
catch(…)
{
}
Слайд 13Иерархия классов исключений и секция catch
try
{
throw SpecialException();
} catch (BaseException
exc)
{
cout << “Error occurred, code = ”
<< exc.errorCode() << endl;
}
class BaseException
{
public:
virtual ~BaseException() { }
virtual int errorCode() const { return -1; }
};
class SpecialException : public BaseException
{
public:
virtual int errorCode() const { return -10; }
};
Слайд 14Иерархия классов исключений и секция catch
try
{
throw SpecialException();
} catch (const
BaseException & exc)
{
cout << “Error occurred, code = ”
<< exc.errorCode() << endl;
}
class BaseException
{
public:
virtual ~BaseException() { }
virtual int errorCode() const { return -1; }
};
class SpecialException : public BaseException
{
public:
virtual int errorCode() const { return -10; }
};
Слайд 15Принцип RAII и исключения
RAII - Resource Acquisition Is Initialization
Слайд 16bool fun()
{
…
Dictionary *
dict = new Dictionary();
If (!dict->loadDictionary(path)) {
delete dict;
return false;
}
EncodingConverter * converter = new EncodingConverter ();
If (!converter->init()) {
delete dict;
delete converter ;
return false;
}
OperationController *controller = new OperationController(dict, converter);
If (!controller->init()) {
delete dict;
delete converter ;
delete controller ;
return false;
}
// некоторые действия и также вызов освобождение dict, converter , controller
}
Слайд 17void fun()
{
// захватить ресурс 1
// захватить ресурс 2
…
// захватить ресурс N
// использование ресурсов
// освободить ресурс 1
// освободить ресурс 2
…
// освободить ресурс N
}
Слайд 18Решение 1
bool fun()
{
…
Dictionary
* dict = new Dictionary();
EncodingConverter * converter = new EncodingConverter ();
OperationController controller = new OperationController(dict, converter);
try
{
If (!dict->loadDictionary(path))
throw DictionaryInitError();
…
} catch(…)
{
delete dict;
delete converter ;
delete controller ;
return false;
}
delete dict;
…
return true;
}
Слайд 19Решение 2
class AutoDictionary
{
public:
AutoDictionary(Dictionary * dict) : mDict(dict)
{ }
~AutoDictionary() { delete mDict;}
Dictionary *operator->() { return mDict; }
private:
Dictionary * mDict;
};
Слайд 20Решение 2
void fun()
{
…
AutoDictionary
dict(new Dictionary());
AutoEncodingConverter converter(new EncodingConverter ());
AutoOperationController controller (new OperationController(dict.get(), converter.get()));
if (!dict->loadDictionary(path))
throw DictionaryInitError();
if (!converter->init())
throw ConverterInitError();
if (!controller->init())
throw ControllerinitError();
…
}
Слайд 21Решение 3
void fun()
{
…
std::auto_ptr
dict(new Dictionary());
std::auto_ptr converter(new EncodingConverter ());
std::auto_ptr controller (new OperationController(dict.get(), converter.get()));
If (!dict->loadDictionary(path))
throw DictionaryInitError();
If (!converter->init())
throw ConverterInitError();
If (If (!controller->init())
throw ControllerinitError();
…
}
Слайд 22bool fun()
{
…
std::auto_ptr dict(new Dictionary());
std::auto_ptr converter(new EncodingConverter ());
std::auto_ptr controller (new OperationController(dict.get(), converter.get()));
if
(!dict->loadDictionary(path))
return false;
if (!converter->init())
return false;
if (!controller->init())
return false;
…
}
Слайд 23преобразования типов
Допускается для:
Для наследуемых типов;
Из типизированного в нетипизированный указатель (void *
);
try
{
throw “Error”; // тип cont char *
throw new std::runtime_error(“”); // тип std::runtime_error *
}
catch(void * ptr) // перехватить все исключения типа “указатель”
{
…
}
Слайд 24Неперехваченные исключения
int main()
{
throw 4;
}
int main()
{
try
{
…
} catch(…)
{
…
}
}
std::terminate()
-> abort()
Слайд 25
Спецификация исключений на уровне определения функций
void fun() throw (x1, x2);
void fun()
throw (std::runtime_error, int);
void fun()
{
try
{
}
catch (std::runtime_error) { throw; }
catch (int) { throw; }
catch(…)
{
std::unexpected();
}
}
std::unexpected() - >std::terminate() -> abort()
Слайд 26void f(); // может генерировать любое исключение
void f() throw(); // не
генерирует исключений
void f() throw(std::exception, int); // генерирует исключения типа int, std::exception и
// производные от std::exception
void fun() throw()
{
throw 1;
}
int main()
{
fun();
return 0;
}
Слайд 27class A
{
public:
virtual ~A() { }
virtual void f();
virtual void g() throw(X, Y);
virtual void h() throw(X);
};
class B : public A
{
public:
virtual void f() throw(X);
virtual void g() throw(X);
virtual void h() throw(X, Y);
};
Слайд 28Неожиданные исключения и std::bad_exception
1. std:: bad_exception
void f() throw(X, std::bad_exception)
{
…
throw 1; //выбрасываем
исключение типа int (будет выброшено std::bad_exception)
…
}
2. unexpected_handler
typedef void(*unexpected_handler);
unexpected_handler set_unexpected(unexpected_handler) throw( );
Слайд 29Исключения в конструкторах
Без использования исключений:
Сконструировать объект с некорректным состоянием
class A
{
public:
A();
bool isValidConstructed() const;
};
…
A a;
If (!a. isValidConstructed())
{
…
}
…
Слайд 302. Присвоить значение глобальной переменной;
3. Не делать инициализации в конструкторе и
сделать отдельную функцию инициализации.
class A
{
public:
A() {}
bool init();
};
Слайд 31Пометить объект как неинициализированный и во всех функциях проверять состояние объекта;
…
const
int gErrCodeInitError=-2;
…
class A
{
public:
A()
{
…
mInitialized = false;
…
}
int fun()
{
If (! mInitialized )
return gErrCodeInitError;
…
}
private:
bool mInitialized;
};
Слайд 32class DBConnectionWrapper
{
public:
DBConnectionWrapper(const std:string & username, const std::string & psw)
: mConnection(NULL)
{
mConnection = new db::DBConnection();
std::string error;
if (mConnection->connect(username, psw, error))
throw DBConnectionFailed(error);
}
…
private:
db::DBConnection * mConnection;
};
try
{
DBConnectionWrapper dbConnectionWrapper (username, psw);
…
} catch(DBConnectionFailed & exc)
…
Слайд 33class DBConnectionWrapper
{
public:
DBConnectionWrapper(const std:string & username, const std::string & psw)
: mConnection(NULL)
{
mConnection = new db::DBConnection();
std::string error;
if (mConnection->connect(username, psw, error))
throw DBConnectionFailed(error);
}
~ DBConnectionWrapper()
{
delete mConnection; // Не сработает, если в конструкторе было исключение
}
…
private:
db::DBConnection *mConnection;
};
Удаляются только полностью сконструированные объекты!!!
Слайд 34class DBConnectionWrapper
{
public:
DBConnectionWrapper(const std:string & username, const std::string & psw)
: mConnection(NULL)
{
try
{
mConnection = new db::DBConnection();
std::string error;
if (mConnection->connect(username, psw, error))
throw DBConnectionFailed(error);
} catch(…)
{
delete mConnection ;
throw;
}
}
…
private:
db::DBConnection *mConnection;
};
Слайд 35class DBConnectionWrapper
{
public:
DBConnectionWrapper(const std:string & username, const std::string & psw)
try
: mConnection(NULL)
{
mConnection = new db::DBConnection();
std::string error;
if (mConnection->connect(username, psw, error))
throw DBConnectionFailed(error);
} catch(…)
{
delete mConnection ;
throw;
}
…
private:
db::DBConnection *mConnection;
};
Слайд 36class DBConnectionWrapper {
public:
DBConnectionWrapper(const std:string & username, const std::string &
psw)
: mConnection(new db::DBConnection())
{
std::string error;
if (mConnection->connect(username, psw, error))
throw DBConnectionFailed(error);
}
…
private:
AutoConnection mConnection;
};
AutoConnection {
public:
AutoConnection(db::DBConnection *connection) : mConnection(connection) { }
~AutoConnection() { delete mConnection; }
db::DBConnection * operator->() { return mConnection; }
private:
db::DBConnection * mConnection;
};
Слайд 37Использование smart pointer
class DBConnectionWrapper
{
public:
DBConnectionWrapper(const std:string & username, const std::string
& psw)
: mConnection(new db::DBConnection())
{
std::string error;
if (mConnection->connect(username, psw, error))
throw DBConnectionFailed(error);
}
…
private:
std::auto_ptr< db::DBConnection> mConnection;
};
Слайд 38Исключения в деструкторах
class DBConnectionWrapper
{
public:
DBConnectionWrapper(const std:string & username, const std::string
& psw);
~DBConnectionWrapper()
{
mConnection->close(error); // может быть сгенерировано исключение
}
private:
std::auto_ptr< db::DBConnection> mConnection;
};
Деструктор объекта вызывается в следующих случаях:
Выход из области видимости или оператор delete;
В процессе обработки исключения: в процессе раскручивания стека;
Слайд 39Деструктор объекта вызывается в следующих случаях:
Выход из области видимости или оператор
delete;
В процессе обработки исключения: в процессе раскручивания стека;
void makeQuery(std::string sql)
{
…
DBConnectionWrapper connection(username, psw);
…
If (!connection.query(sql))
throw QueryFailed(); // автоматически будет вызван ~DBConnectionWrapper
…
}
Слайд 40class DBConnectionWrapper
{
public:
DBConnectionWrapper(const std:string & username, const std::string & psw);
~DBConnectionWrapper()
{
try
{
mConnection->close(error); // может быть сгенерировано исключение
} catch(…)
{
}
}
private:
std::auto_ptr< db::DBConnection> mConnection;
};
Слайд 41Стандартные исключения
exception
logic_error
runtime_error
length_error
domain_error
out_of_range
invalid_argument
bad_alloc
bad_exception
io_base::failure
bad_typeid
bad_cast
range_error
overflow_error
underflow_error
Слайд 421. std::bad_alloc
SomeClass sc = new SomeClass(); //
может быть сгенерировано исключение std::bad_alloc
2. std::bad_cast
B & b = dynamic_cast(a); // может быть сгенерировано исключение std::bad_cast
3. std::bad_exception
Для спецификации исключений
Слайд 43Гарантия безопасности исключений
Уровни гарантий:
1. no throw guarantee;
2. strong exception safety (commit
or rollback semantics);
3. basic exception safety;
4. Minimal exception safety;
5. No exception safety;
Слайд 44class Node
{
int mData;
Node * mPNext;
}
class IntList
{
public:
…
void add(int value)
{
mCachedSize ++;
Node *
node = new Node(); // возможно исключение std::bad_alloc
…
}
…
Unsigned int size() const { return mCachedSize; }
private:
…
unsigned int mCachedSize;
};
1. no throw guarantee;
2. strong exception safety (commit or rollback semantics);
3. basic exception safety;
4. Minimal exception safety;
5. No exception safety;
Слайд 45class Node
{
int mData;
Node * mPNext;
}
class IntList
{
public:
…
void add(int value) throw (std::bad_alloc)
{
mCachedSize
++; // некорректное значение размера
Node * node = new Node(); // возможно исключение std::bad_alloc
…
}
…
Unsigned int size() const { return mCachedSize; }
private:
…
unsigned int mCachedSize;
};
1. no throw guarantee;
2. strong exception safety (commit or rollback semantics);
3. basic exception safety;
4. Minimal exception safety;
5. No exception safety;
Слайд 46class Node
{
int mData;
Node * mPNext;
}
class IntList
{
public:
…
void add(int value) throw (std::bad_alloc)
{
Node *
node = new Node(); // возможно исключение std::bad_alloc
…
mCachedSize ++; // Увеличить размер только после полностью завершенной
// операции
}
…
Unsigned int size() const { return mCachedSize; }
private:
…
unsigned int mCachedSize;
};
1. no throw guarantee;
2. strong exception safety (commit or rollback semantics);
3. basic exception safety;
4. Minimal exception safety;
5. No exception safety;
Слайд 47class T1;
class T2;
class A
{
public:
…
A & operator=(const A & obj)
{
mT1 =
obj.mT1;
mT2 = obj.mT2;
return *this;
}
…
private:
T1 mT1;
T2 mT2;
};
1. no throw guarantee;
2. strong exception safety (commit or rollback semantics);
3. basic exception safety;
4. Minimal exception safety;
5. No exception safety;
Гарантия безопасности и operator=
Слайд 48class T1;
class T2;
class A
{
public:
…
A & operator=(const A & obj)
{
mT1 =
obj.mT1;
mT2 = obj.mT2; // может произойти исключение
return *this;
}
…
private:
T1 mT1;
T2 mT2;
};
1. no throw guarantee;
2. strong exception safety (commit or rollback semantics);
3. basic exception safety;
4. Minimal exception safety;
5. No exception safety;
Гарантия безопасности и operator=
Слайд 49class A
{
public:
…
A & operator=(const A & obj)
{
A tmp(obj);
swap(tmp);
return *this;
}
…
private:
void swap(A
& a) throw();
T1 mT1;
T2 mT2;
};
1. no throw guarantee;
2. strong exception safety (commit or rollback semantics);
3. basic exception safety;
4. Minimal exception safety;
5. No exception safety;
Гарантия безопасности и operator=