目录
前言
C++语言能够解决的问题规模千变万化,有的小到一个程序员几小时能够完成,有的是含有几千几万行代码的庞大系统,需要几百个程序员协同工作好几年,当我们编写比较复杂的管理系统时,异常就十分的有用
一、C语言传统的异常处理
C语言的异常处理机制有很多的缺点
二、C++异常概念
1、基本概念
try
{
/* code */
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
在C++语言中,我们通过抛出一条表达式来引发异常,被抛出的表达式类型及当前的调用链共同决定了哪段处理代码将用来处理异常。被选中的代码段是在调用链与被抛出对象类型匹配且最近的处理代码。
当执行throw时,跟在throw后面的语句将不再被执行,这也就是将该函数提前结束,类似与return
一旦程序开始执行异常处理代码,则沿着调用链创建的对象被销毁,因为跟在throw后面的语句将不再被执行。
2、异常的抛出和匹配原则
3、函数调用链中异常栈展开匹配原则
三、异常的使用
1、基本用法
在使用异常之前,虽然可以throw任何类型,但是C++建议最好要抛一个异常类对象
一个最简单的异常类,应该包括:错误码,错误信息
并且至少要有拷贝构造,析构函数,移动构造
一般的异常抛出与接受需要类型匹配,这里抛出派生类也可以,catch时使用异常类基类的引用来接收,构成多态
class Exception
{
public:
Exception(const string &errmsg, int id)
: _errmsg(errmsg), _id(id)
{
}
virtual string what() const
{
return _errmsg;
}
virtual int GetId() const
{
return _id;
}
protected:
string _errmsg;
int _id;
};
int division(int a, int b)
{
if (b == 0)
{
throw Exception("除数为0", -1);
}
return a / b;
}
int main()
{
int a = 0, b = 0;
while (true)
{
try
{
cin >> a >> b;
cout << division(a, b) << endl;
}
catch (const Exception &e)
{
cout << "错误码为: " << e.GetId() << endl;
cout << "错误信息为: " << e.what() << endl;
}
catch (...)
{
cout << "未知异常" << endl;
}
}
return 0;
}
2、重新抛出
在实际开发过程中异常是不可缺少的存在,但是一出现异常就直接返回到外部,也不太好
例如:在我们使用微信时,因为网络情况较差,不尝试多次直接抛出异常,对于用户来说那是接受不了的事情。所有这里就有重新抛出的概念
这里是模拟实现的发微信的过程
class Exception
{
public:
Exception(const string &errmsg, int id)
: _errmsg(errmsg), _id(id)
{
}
virtual string what() const
{
return _errmsg;
}
virtual int GetId() const
{
return _id;
}
protected:
string _errmsg;
int _id;
};
class SqlException : public Exception
{
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
{
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
{
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(const string &msg)
{
srand(time(0));
if (rand() % 7 == 0)
{
throw SqlException("权限不足", 100, "select * from name = '张三'");
}
cout << "信息发送成功 ->" << msg << endl;
}
void SendMsg(const string &msg)
{
srand(time(0));
if(rand() % 2 == 0)
{
throw Exception("网络错误", 200);
}
SQLMgr(msg);
}
void HttpServer()
{
string msg("今晚一起看电影吗?");
int n = 3;
while(n--)
{
try
{
SendMsg(msg);
}
catch(const Exception& e)
{
if(e.GetId() == 200 && n > 0)
{
continue;
}
else
{
throw;
}
}
}
}
int main()
{
while (1)
{
sleep(1);
try
{
HttpServer();
}
catch (const Exception &e)
{
cout << e.what() << endl;
}
catch (...)
{
cout << "未知异常" << endl;
}
}
return 0;
}
细心的同学可能会注意到,重新抛出时直接使用了throw而没有指明对象。
这里是因为catch无需访问抛出表达式,则可以忽略形参的名字,并且这个形参可以是左值引用,而不可以是右值引用。空throw只能出现在catch或catch直接或间接调用的函数中
四、异常的规范
// 这里表示这个函数会抛出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;
虽然C++11提供了noexcept关键字,但是编译器不会在编译时检查noexcept。事实上如果一个函数在说明了noexcept的同时又含有throw语句或者调用了可能抛出异常的其它函数,编译器将会顺利通过,并不会因为违反了异常的说明的情况而报错
五、异常的优缺点
1、异常的优点
2、异常的缺点
总结
以上就是今天要讲的内容,本文仅仅简单介绍了C++异常