一、传统错误处理办法
1.终止程序(段错误)
2.返回错误码
3.返回合法值,让程序处于某种非法状态
4.调用一个预先设置的出现错误时调用的函数-回调函数
二.异常处理
1.异常定义
异常:当一个函数发现自己无法处理的错误时抛出异常,让函数调用者直接或间接的处理异常
2.异常的处理与捕获
1.异常是
通过抛出对象而引发
的,该对象的类型
决定了应该激活哪一处理代码
2.被选中的处理代码是调用链中与该对象类型匹配的且与抛出异常位置最近的哪一个
3.抛出异常后释放局部存储对象
,所以被抛出的对象也就还给系统了,throw会初始化一个抛出特殊的异常对象副本(匿名对象),
异常对象由编译管理,异常对象再传给对应的catch处理后撤销
3.栈展开
抛出异常时,将暂停当前函数的执行,开始查找对应的匹配catch即捕获异常的子句
首先检查throw自身是否存在catch块内部,若是在查找匹配的catch语句,若有匹配的,则处理之。没有的话退出当前函数栈,继续在调用函数的栈中进行查找。不断重复上述过程。如果到达main函数的栈,依旧无匹配的,终止程序。
上述过程称为栈展开
。找到匹配的catch子句并处理后,会继续沿着子句后的语句继续执行
4.异常捕获的匹配规则
异常对象的类型必须与catch说明符的类型完全匹配
。以下除外:
1.允许从非const对象到const的转换
2.允许从派生类到基类的转换
3.将数组转换为指向数组类型的指针,将函数转换为指向函数类型的指针
class Exception
{
public:
Exception(int errId, const char * errMsg)
: _errId(errId)
, _errMsg(errMsg)
{}
void What() const
{
cout << "errId:" << _errId << endl;
cout << "errMsg:" << _errMsg << endl;
}
private:
int _errId; // 错误码
string _errMsg; // 错误消息
};
void Func1(bool isThrow)
{
if (isThrow)
{
throw Exception(1, "抛出Exception对象");
}
printf("Func1(%d)\n", isThrow);
}
void Func2(bool isThrowString, bool isThrowInt)
{
if (isThrowString)
{
throw string("抛出string对象");
}
if (isThrowInt)
{
throw 7;
}
printf("Func2(%d,%d)\n", isThrowString, isThrowInt);
}
void Func()
{
try
{
Func1(false);
Func2(true, false);
}
catch (const string& errMsg)
{
cout << "Catch string Object:" << errMsg << endl;
}
catch (const int errId)
{
cout << "Catch int Object:" << errId<< endl;
}
catch (const Exception& e)
{
e.What();
}
catch (...)
{
cout << "未知异常" << endl;
}
printf("Func()\n");
}
5.异常的重新抛出
或许单个的catch不能完全捕获异常,在进行一些校正处理后,希望交给更外层的调用链函数处理,catch则可通过重新抛出将异常传递给上一层函数进行处理
class Exception
{
public:
Exception(int errId=0, const char * errMsg=" ")
: _errId(errId)
, _errMsg(errMsg)
{}
void What() const
{
cout << "errId:" << _errId << endl;
cout << "errMsg:" << _errMsg << endl;
}
private:
int _errId; // 错误码
string _errMsg; // 错误消息
};
void Func1( )
{
throw string("Throw Func1 string");
}
void Func2()
{
try
{
Func1();
}
catch (string& errMsg)
{
cout << errMsg << endl;
}
}
void Func3()
{
try
{
Func2();
}
catch (Exception & e)
{
e.What();
}
}
6.异常与构造函数&析构函数
1 . 构造函数完成对象的构造和初始化,需要
保证不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化
。
2 . 析构函数主要完成资源的清理,需要保证不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等
)
7.c++标准库定义的exception类
exception类是C++ 定义的一个标准异常的类,通常我们通过继承exception类定义合适的异常类
class Exception1 : public exception
{
public:
Exception1(int errId = 0, const char * errMsg = "")
: _errId(errId)
, _errMsg(errMsg)
{}
virtual const char* what() const
{
cout << "errId:" << _errId << endl;
cout << "errMsg:" << _errMsg << endl;
return _errMsg.c_str();
}
private:
int _errId; // 错误码
string _errMsg; // 错误消息
};
class Exception2 : public exception
{
public:
Exception2(int errId = 0, const char * errMsg = "")
: _errId(errId)
, _errMsg(errMsg)
{}
virtual const char* what() const
{
cout << "errId:" << _errId << endl;
cout << "errMsg:" << _errMsg << endl;
return _errMsg.c_str();
}
private:
int _errId; // 错误码
string _errMsg; // 错误消息
};
void Func()
{
try
{
throw Exception1(1, "Exception1");
throw Exception1(2, "Exception2");
}
catch (exception & e)
{
e.what();
}
}
void Func1()
{
try{
int* p = new int[0x7fffffff / 4];
}
catch (std::bad_alloc&ba)
{
std::cout << "bad_alloc caught: " << ba.what() << '\n' << endl;
}
catch (exception & e)
{
std::cout << "exception<-bad_alloc caught: " << e.what() << '\n';
}
}
三、异常的优缺点
优点
1异常允许上层应用决定如何处理在底层嵌套函数中发生的“不可能发生的”失败.,不像出错代码的记录那么难理解
2.引入异常,使得c++与python及其他语言更加兼容
3.许多第三方库使用异常,关闭异常将导致难以与之结合
4.是解决构造函数失败的唯一解决方案
5.在测试框架中,很好用
缺点
1.执行乱跳,不方便调试跟踪代码
2.异常安全要使用RAII和不同编码实践。加大了代码量,需要大量支持
四、模拟一个项目实现异常
class Exception
{
public:
Exception(const string& msg, int id)
:_errMsg(msg)
, _errId(id)
{}
virtual string what() = 0;
protected:
string _errMsg;
int _errId;
};
//连接数据库异常
class ConnectException : public Exception
{
public:
ConnectException(const string& Msg, int id)
:Exception(Msg, id)
{}
virtual string what()
{
string msg = "数据库错误:";
msg += _errMsg;
return msg;
}
};
//获取数据异常
class GetinfoException : public Exception
{
public:
GetinfoException(const string& Msg, int id)
:Exception(Msg, id)
{}
virtual string what()
{
string msg = "获取数据数据错误:";
msg += _errMsg;
return msg;
}
};
class EchoException : public Exception//响应异常
{
public:
EchoException(const string& Msg, int id)
:Exception(Msg, id)
{}
virtual string what()
{
string msg = "响应错误:";
msg += _errMsg;
return msg;
}
};
void ConnectSql()
{
if (rand() % 5 == 0) //这里是模拟函数产生异常,以下相同
{
throw ConnectException("数据库连接错误", 1);
}
if (rand() % 3 == 0)
{
throw ConnectException("网络错误", 2);
}
cout << "OK 数据库连接成功" << endl;
}
void GetInfo()
{
if (rand() % 2 == 0)
{
throw GetinfoException("获取数据失败", 101);
}
if (rand() % 3 == 0)
{
throw GetinfoException("数据处理错误", 102);
}
cout << "OK 获取客户端信息成功" << endl;
}
void EchoClient()
{
if (rand() % 2 == 0)
{
throw EchoException("响应错误", 202);
}
if (rand() % 3 == 0)
{
throw EchoException("数据丢失", 203);
}
cout << "OK 响应客户端成功" << endl;
}
class HTTPserver
{
public:
void start()
{
while (1)
{
try
{
ConnectSql();
Sleep(1000);
GetInfo();
Sleep(1000);
EchoClient();
Sleep(1000);
}
catch (Exception& e)
{
cout << e.what() << endl;
}
}
}
};
int main()
{
HTTPserver h;
h.start();
system("pause");
return 0;
}
结果如图: