文章目录
1 C++中的异常处理基础
1.1 C++中异常处理简介
C++内置了异常处理的语法元素try…catch…:
- try语句处理正常逻辑代码。
- catch语句处理异常情况。
- try语句中的异常由对应的catch语句处理。

C++中通过throw语句抛出异常:

C++异常处理分析:
throw抛出的异常必须被catch处理:
- 当前函数能够处理异常,程序继续往下执行。
- 当前函数无法处理异常,则函数停止执行,并返回。

编程实验:C++异常处理初探
#include <iostream>
#include <string>
using namespace std;
double divide(double a, double b)
{
const double delta = 0.000000000000001;
double ret = 0;
if( !((-delta < b) && (b < delta)) )
{
ret = a / b;
}
else
{
throw 0;
}
return ret;
}
int main(int argc, char *argv[])
{
try
{
double r = divide(1, 0);
cout << "r = " << r << endl;
}
catch(...)
{
cout << "Divided by zero..." << endl;
}
return 0;
}
1.2 异常类型匹配
同一个try语句可以跟上多个catch语句:
- catch语句可以定义具体处理的异常类型。
- 不同类型的异常由不同的catch语句负责处理。
- try语句中可以抛出任何类型的异常。
- catch(…)用于处理所有类型的异常,并且只能放在最后。
- 任何异常都只能被捕获(catch)一次。
异常处理的匹配规则:

编程实验:异常类型匹配
#include <iostream>
#include <string>
using namespace std;
void Demo1()
{
try
{
throw 'c';
}
catch(char c)
{
cout << "catch(char c)" << endl;
}
catch(short c)
{
cout << "catch(short c)" << endl;
}
catch(double c)
{
cout << "catch(double c)" << endl;
}
catch(...)
{
cout << "catch(...)" << endl;
}
}
void Demo2()
{
throw string("D.T.Software");
}
int main(int argc, char *argv[])
{
Demo1();
try
{
Demo2();
}
catch(char* s)
{
cout << "catch(char *s)" << endl;
}
catch(const char* cs) //严格匹配类型
{
cout << "catch(const char *cs)" << endl;
}
catch(string ss)
{
cout << "catch(string ss)" << endl;
}
return 0;
}
1.3 异常处理的另一种写法
异常处理还可以写成如下形式:
- try…catch用于分割正常功能代码与异常处理代码。
- try…catch可以直接将函数实现分割为2部分。

1.4 异常被处理后局部对象的析构函数会被调用吗
看如下代码:
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal()" << endl;
}
~Animal()
{
cout << "~Animal()" << endl;
}
};
void demo()
{
Animal a;
throw 0;
}
int main()
{
try
{
demo();
}
catch (...)
{
cout << "...";
}
return 0;
}
输出结果如下:

但是如果不捕捉异常的话,就不会调用抛出异常前的局部对象的析构函数。
2 异常的二次抛出
2.1 catch中抛出基本类型异常
catch语句块中可以抛出异常:

catch中捕获的异常可以被重新解释后抛出,工程中使用这样的方式统一异常类型。

编程实验:异常的重新解释
#include <iostream>
#include <string>
using namespace std;
void Demo()
{
try
{
try
{
throw 'c';
}
catch(int i)
{
cout << "Inner: catch(int i)" << endl;
throw i;
}
catch(...)
{
cout << "Inner: catch(...)" << endl;
throw;
}
}
catch(...)
{
cout << "Outer: catch(...)" << endl;
}
}
/*
假设: 当前的函数式第三方库中的函数,因此,我们无法修改源代码
函数名: void func(int i)
抛出异常的类型: int
-1 ==》 参数异常
-2 ==》 运行异常
-3 ==》 超时异常
*/
void func(int i)
{
if( i < 0 )
{
throw -1;
}
if( i > 100 )
{
throw -2;
}
if( i == 11 )
{
throw -3;
}
cout << "Run func..." << endl;
}
void MyFunc(int i)
{
try
{
func(i);
}
catch(int i)
{
switch(i)
{
case -1:
throw "Invalid Parameter";
break;
case -2:
throw "Runtime Exception";
break;
case -3:
throw "Timeout Exception";
break;
}
}
}
int main(int argc, char *argv[])
{
// Demo();
try
{
MyFunc(11);
}
catch(const char* cs)
{
cout << "Exception Info: " << cs << endl;
}
return 0;
}
2.2 catch中抛出自定义类型异常
catch中可以抛出自定义类型异常:
- 异常的类型可以是自定义类型。
- 对于类类型异常的匹配仍旧是至上而下严格匹配的。
- 赋值兼容性原则在异常匹配中依然适用。
- 一般而言:
- 匹配子类异常的catch放在上部。
- 匹配父类异常的catch放在下部。
自定义异常类型的作用:
- 在工程中会定义一系列的异常类。
- 每个类代表工程中可能出现的一种异常类型。
- 代码复用时可能需要重解释不同的异常类。
- 在定义catch语句块时推荐使用引用作为参数。
编程实验:类类型的异常
#include <iostream>
#include <string>
using namespace std;
class Base
{
};
class Exception : public Base
{
int m_id;
string m_desc;
public:
Exception(int id, string desc)
{
m_id = id;
m_desc = desc;
}
int id() const
{
return m_id;
}
string description() const
{
return m_desc;
}
};
/*
假设: 当前的函数式第三方库中的函数,因此,我们无法修改源代码
函数名: void func(int i)
抛出异常的类型: int
-1 ==》 参数异常
-2 ==》 运行异常
-3 ==》 超时异常
*/
void func(int i)
{
if( i < 0 )
{
throw -1;
}
if( i > 100 )
{
throw -2;
}
if( i == 11 )
{
throw -3;
}
cout << "Run func..." << endl;
}
void MyFunc(int i)
{
try
{
func(i);
}
catch(int i)
{
switch(i)
{
case -1:
throw Exception(-1, "Invalid Parameter");
break;
case -2:
throw Exception(-2, "Runtime Exception");
break;
case -3:
throw Exception(-3, "Timeout Exception");
break;
}
}
}
int main(int argc, char *argv[])
{
try
{
MyFunc(11);
}
catch(const Exception& e)
{
cout << "Exception Info: " << endl;
cout << " ID: " << e.id() << endl;
cout << " Description: " << e.description() << endl;
}
catch(const Base& e)
{
cout << "catch(const Base& e)" << endl;
}
return 0;
}
3 标准库中的异常
C++标准库中提供了实用的异常类族:
- 标准库中的异常都是从exception类派生的。
- exception类有两个主要的分支:
- logic_error:常用于程序中的可避免逻辑错误。
- runtime_error:常用于程序中无法避免的恶性错误。
标准库中的异常:

4 terminate函数
先思考一个问题:如果在main函数抛出异常会发生什么?并且这个异常不进行处理,最后会传到哪里?

下面的代码输出什么?

输出“Test()”后程序运行结束,不同的平台输出的字符串不同,Windows平台会弹出程序异常的对话框。
异常的最终处理者:
- 如果异常无法被处理,terminate()结束函数会被自动调用,结束整个程序。terminate()是整个程序释放系统资源的最后机会。
- 默认情况下,terminate()调用库函数abort()终止程序。
- abort()函数使得程序执行异常而立即退出。
- C++支持替换默认的terminate()函数实现。
terminate()函数的替换:
自定义一个无返回值无参数的函数:
- 不能抛出任何异常。
- 必须以某种方式结束当前程序。
调用set_terminate(0设置自定义的结束函数:
- 参数类型为void(*)()。
- 返回值为默认的terminate()函数入口地址。
编程实验:自定义结束函数
#include <iostream>
#include <cstdlib>
#include <exception>
using namespace std;
void my_terminate()
{
cout << "void my_terminate()" << endl;
exit(1); //调用exit函数会确保所有的全局对象和静态局部对象的析构函数被调用,abort()函数不会
}
class Test
{
public:
Test()
{
cout << "Test()";
cout << endl;
}
~Test()
{
cout << "~Test()";
cout << endl;
}
};
int main()
{
set_terminate(my_terminate);
static Test t;
throw 1;
return 0;
}
5 函数的异常规格说明
思考一个问题:如何判断一个函数是否会抛出异常,以及抛出哪些异常?
函数的异常规格说明:
- C++提供语法用于声明函数所抛出的异常。
- 异常声明作为函数声明的修饰符,写在参数列表后面。

异常规格说明的意义:
- 提示函数调用者必须做好异常处理的准备。
- 提示函数的维护者不要抛出其他异常。
- 异常规格说明是函数接口的一部分。
- 函数声明和实现处都要写好上相同的规格声明。
问题:如果抛出的异常不在声明列表中中,会发生什么?

如上的代码,对于g++和bcc编译器,程序直接终止运行,而vs编译器则以char类型的异常捕获。
抛出的异常不在规格说明中:
- 函数抛出的异常不在规格说明中,全局unexpected()函数被调用。
- 默认的unexpected()函数会调用全局的terminate()函数。
- 可以自定义函数替换默认的unexpected()函数实现。
- 注意:不是所有的C++编译器都支持这个标准行为。
unexpected()函数的替换:
自定义一个无返回值无参数的函数:
- 能够再次抛出异常:
- 当异常符合触发函数的异常规格说明时,恢复程序执行。
- 否则,调用全局terminate()函数结束程序。
- 调用set_unecpected()设置自定义的异常函数:
- 参数类型为void(*)()。
- 返回值为默认的unexceptd()函数入口地址。
编程实验:自定义unexpected()函数
#include <iostream>
#include <cstdlib>
#include <exception>
using namespace std;
void my_unexpected()
{
cout << "void my_unexpected()" << endl;
// exit(1);
throw 1;
}
void func() throw(int)
{
cout << "func()";
cout << endl;
throw 'c';
}
int main()
{
set_unexpected(my_unexpected);
try
{
func();
}
catch(int)
{
cout << "catch(int)";
cout << endl;
}
catch(char)
{
cout << "catch(char)";
cout << endl;
}
return 0;
}
6 异常的生命周期
对于我们在各个地方抛出的异常,实际上都是抛出了一个匿名变量,不管是基本类型,还是自定义类型。这个匿名变量的生命周期出了cathc语句块就会结束。
对于如下几种形式:
int a = 0;
throw a; // 实际上会生成一个匿名的临时变量
throw 1; // 直接是匿名的临时变量
throw Exception(); // 生成匿名对象
Exception e;
throw e; // 会生成匿名对象,对e进行拷贝
对于如下代码:
#include <iostream>
using namespace std;
class Exception
{
public:
Exception()
{
cout << "Exception() : " << this << endl;
}
Exception(const Exception& obj)
{
cout << "Exception(const Exception& obj) : " << this << endl;
}
~Exception()
{
cout << "~Exception() : " << this << endl;
}
};
void test36()
{
int a = 123;
try
{
throw Exception();
}
catch (const Exception& e)
{
cout << "&e : " << &e << endl;
}
}
输出结果为:

参考资料:
17万+

被折叠的 条评论
为什么被折叠?



