目录
1、C语言传统的错误处理方式
传统的错误处理机制:
1.
终止程序,如
assert
,缺陷:用户难以接受。如发生内存错误,除
0
错误时就会终止程序。
2.
返回错误码
,缺陷:需要程序员自己去查找对应的错误。如系统的很多库的接口函数都是通
过把错误码放到
errno
中,表示错误
实际中
C
语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的
错误。
2、C++异常概念
只有抛异常才会执行catch,否则不会执行;catch匹配的是就近原则。catch执行完之后面的代码正常执行。
3、异常的用法
3.1、异常的抛出与捕获
3.1.1、在函数调用链中异常栈展开匹配原则
1.
首先
检查
throw
本身是否在
try
块内部,如果是再查找匹配的
catch
语句
。如果有匹配的,则
调到
catch
的地方进行处理。
2.
没有匹配的
catch
则退出当前函数栈,继续在调用函数的栈中进行查找匹配的
catch
。
3.
如果到达
main
函数的栈,依旧没有匹配的,则终止程序
。上述这个沿着调用链查找匹配的
catch
子句的过程称为
栈展开
。所以实际中我们最后都要加一个
catch(...)
捕获任意类型的异
常,否则当有异常没捕获,程序就会直接终止。
4.
找到匹配的
catch
子句并处理以后,会继续沿着
catch
子句后面继续执行。
3.1.2、异常的抛出和匹配原则
1.
异常是通过
抛出对象而引发
的,该
对象的类型
决定了应该激活哪个
catch
的处理代码。
2.
被
选中的处理代码
是调用链中
与该对象类型匹配且离抛出异常位置最近
的那一个。
3.
抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,
所以会生成一个拷贝对象,这个拷贝的临时对象会在被
catch
以后销毁。(这里的处理类似
于函数的传值返回)
4. catch(...)
可以捕获任意类型的异常,问题是不知道异常错误是什么。
5.
实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,
可以抛出的派生类对象,
使用基类捕获,这个在实际中非常实用。
3.3、异常规范
//
这里表示这个函数会抛出
A/B/C/D
中的某种类型的异常
void
fun
()
throw
(
A
,
B
,
C
,
D
);
//
这里表示这个函数只会抛出
bad_alloc
的异常
void*
operator new
(
std::size_t size
)
throw
(
std::bad_alloc
);
//
这里表示这个函数不会抛出异常
void*
operator delete
(
std::size_t size
,
void*
ptr
)
throw
();
// C++11
中新增的
noexcept
,表示不会抛异常
thread
()
noexcept
;
thread
(
thread
&&
x
)
noexcept
;
3.4、异常的重新抛出
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
throw "Division by zero condition!";
}
return (double)a / (double)b;
}
void Func()
{
// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。
// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再
// 重新抛出去。
int* array = new int[10];
try {
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
}
catch (...) //有除0错误,所以它来捕获,但是它不处理异常 只是先将内存释放
//释放完之后再将异常抛出交给外部
{
cout << "delete []" << array << endl;
delete[] array;
throw; //异常重新抛出
}
// ...
cout << "delete []" << array << endl;
delete[] array;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg) //接受到重新抛出的异常
{
cout << errmsg << endl; //对异常进行处理
}
return 0;
}
4、自定义异常体系
class Exception //基类
{
public:
Exception(const string& errmsg, int id)
:_errmsg(errmsg)
,_id(id)
{}
virtual string what() const
{
return _errmsg;
}
protected:
string _errmsg;
int _id;
};
class SqlException : public Exception //sql类
{
public:
SqlException(const string& errmsg, int id, const string& sql)
:Exception(errmsg, id)
, _sql(sql)
{}
virtual string what() const
{
string str = "SqlException:";
str += _errmsg;
str += "->";
str += _sql;
return str;
}
private:
const string _sql;
};
class CacheException : public Exception //cache模块
{
public:
CacheException(const string& errmsg, int id)
:Exception(errmsg, id)
{}
virtual string what() const
{
string str = "CacheException:";
str += _errmsg;
return str;
}
};
class HttpServerException : public Exception //http模块
{
public:
HttpServerException(const string& errmsg, int id, const string& type)
:Exception(errmsg, id)
, _type(type)
{}
virtual string what() const
{
string str = "HttpServerException:";
str += _type;
str += ":";
str += _errmsg;
return str;
}
private:
const string _type;
};
void SQLMgr() //sql模拟错误
{
srand(time(0));
if (rand() % 7 == 0)
{
throw SqlException("权限不足", 100, "select * from name = '张三'");
}
//throw "xxxxxx";
}
void CacheMgr() //cache模拟错误
{
srand(time(0));
if (rand() % 5 == 0)
{
throw CacheException("权限不足", 100);
}
else if (rand() % 6 == 0)
{
throw CacheException("数据不存在", 101);
}
SQLMgr();
}
void HttpServer() //http模拟错误
{
// ...
srand(time(0));
if (rand() % 3 == 0)
{
throw HttpServerException("请求资源不存在", 100, "get");
}
else if (rand() % 4 == 0)
{
throw HttpServerException("权限不足", 101, "post");
}
CacheMgr();
}
int main()
{
while (1)
{
this_thread::sleep_for(chrono::seconds(1));
try{
HttpServer();
}
catch (const Exception& e) // 这里捕获父类对象就可以
{
// 多态 直接利用多态 就可以捕获不同类型的异常
cout << e.what() << endl;
}
catch (...)
{
cout << "Unkown Exception" << endl;
}
}
return 0;
}