1. 异常处理
throw异常
往上层找catch,如果没找到就报错,找到就直接跳到catch匹配的位置
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
throw "Division by zero condition!";
else
return ((double)a / (double)b);
}
void Func()
{
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
}
int main()
{
//Func();
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
catch (int errid)// 其中类型也要匹配,抛char* catch int也会报错
{
cout << errid << endl;
}
return 0;
}
多个地方有类型匹配的catch,会跳到近的那里(提前捕获)
同一位置不能允许相同的捕获
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
throw "Division by zero condition!";
else
return ((double)a / (double)b);
}
void Func()
{
try
{
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
}
catch (const char* errmsg)// 跳到近的这里
{
cout << errmsg << endl;
}
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)// 在Func里被捕获了,就不会执行main里的catch
{
cout << errmsg << endl;
}
return 0;
}
如何一眼看出是什么错误
抛个概率异常
size_t x = 0;// 统计抛异常的次数
// 有25%的概率抛异常
void Probability()
{
int val = rand();
if (val<RAND_MAX/4)
{
string str = "25%的概率都给你撞上了->";
str += to_string(val);
throw str;
}
else
{
cout << val << endl;
}
}
void Func()
{
try
{
Probability();
}
catch (const string& s)
{
++x;
cout << s << endl;// 接收到的是str的拷贝,str是临时变量,出作用域会被销毁
}
catch (...)// 捕获没有匹配的任意类型的异常
{
cout << "未知异常" << endl;
}
}
int main()
{
srand(time(0));
for (size_t i = 0; i < 100; i++)// 试100次
{
Func();
}
cout << "抛异常的总数:" << x << endl;
return 0;
}
类型匹配的异常能知道是什么错误
可以抛派生类,用基类捕获(可以切片)
定义个exception基类
operator new[]和operator new继承了exception在malloc失败后能抛异常
// exception的子类抛异常
int main()
{
try
{
size_t i = 0;
while (1)
{
int* myarray = new int[1024 * 100];
cout << myarray << "->" << i++ << endl;
}
}
catch (const exception& ba)
{
std::cerr << "bad_alloc caught: " << ba.what() << '\n';
}
return 0;
}
2. 异常安全
new了要自己delete
如果中间抛异常,就没法delete,导致内存泄漏,直到资源不足才抛异常(异常安全)
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 = nullptr;
array = new int[1024 * 1024];
// 异常安全
try
{
int len, time;
//cin >> len >> time;
len = rand();
time = rand() % 5;
cout << Division(len, time) << endl;
}
catch (...)
{
// 在这里捕获异常是为了释放空间
cout << __LINE__ << " delete []" << array << endl;
delete[] array;
throw; // 捕获什么抛什么
}
cout << __LINE__ << " delete []" << array << endl;
delete[] array;
}
int main()
{
srand(time(0));
while (1)
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
catch (const exception& e)
{
cout << e.what() << endl;
}
}
return 0;
}
但这个方法比较low,之后能用智能指针改进它
3. 异常规范
C++98期望每个人写函数,声明是否抛异常,抛什么异常(规范的期待,但不是必须的)
C++11简化了这块规则,如果你不抛异常,你说一声
4. 异常的优缺点
优点
清晰准确
返回错误码要层层返回,异常在哪catch在哪拿到
很多第三方库都包含异常
部分函数使用异常更好处理,比如构造函数没有返回值,不方便使用错误码方式处理。比如T& operator这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误
缺点
执行流乱跳,很混乱
性能开销
C++没有垃圾回收机制,要用只能指针解决资源管理
C++标准库的异常体系定义得不好(不够全面),导致大家各自定义各自的异常体系,非常的混乱
异常尽量规范使用,否则后果不堪设想,随意抛异常,外层捕获的用户苦不堪言。所以异常规范有两点:一、抛出异常类型都继承自一个基类。二、函数是否抛异常、抛什么异常,都使用 func() throw();的方式规范化。
其他语法,异常机制基本差不多
C++独有缺陷:
标准库定义不好用。不支持看堆栈
C++没有垃圾回收器,所以申请内存和释放内存位置要很小心。容易泄露,需要RAII机制再补充