目录
相比C语言, C++的一个优势是提供了异常处理机制,定义了相关的关键字。
try: 代表可以抛出异常的代码块。
catch: 用于处理抛出的一个特定异常的代码块。
throw: 用于抛出一个异常。也可以用于一个函数抛出的异常列表。
1.为何需要异常处理
下面是异常处理比传统错误处理的优势。
1. 可以将处理错误的代码与正常代码分离开:
传统错误处理代码中,通常包含if else条件判断。这些处理错误的条件代码与正常代码会混合在一起。降低了代码的可读性与可维护性。使用try catch代码块,可以将处理异常的代码与正常代码分离开。
2. 函数/方法可以处理他们所选择的任何异常:
一个函数可以抛出多种异常,但是可以选择只处理其中的一些。另外的一些,可以交由调用者来处理。如果调用者选择不处理这些异常,可以抛给最外层的调用者去处理。
C++中,一个函数可以使用throw关键字来指定它要抛的异常。此函数的调用者必须采取某些方式来处理异常(或者继续指定异常,或者捕获它)。
3. 错误类型的组合:
C++中, 基本类型和对象都能当作异常抛出。我们可以创建异常对象的组合,将异常在namespace或class中分组,按照类型进行分类。
2.C++的异常处理
2.1正常流程
下面是一个简单演示。 输出结果解释了try/catch的执行流程。
#include <iostream>
using namespace std;
int main() {
int x = -1;
// Some code
cout << "Before try \n";
try {
cout << "Inside try \n";
if (x < 0) {
throw x;
cout << "After throw (Never executed) \n";
}
}
catch (int x) {
cout << "Exception Caught \n";
}
cout << "After catch (Will be executed) \n";
return 0;
}
运行结果:
Before try
Inside try
Exception Caught
After catch (Will be executed)
2.2捕获所有异常
有一个特殊的catch代码块,称为'catch call'。catch(...)可以用来捕获所有类型的异常。
例如,下面程序中抛出了一个异常,但是没有catch代码块来捕获int类型,所以catch(...)会捕获并处理。
#include <iostream>
using namespace std;
int main() {
try {
throw 10;
}
catch (char* excp) {
cout << "Caught " << excp;
}
catch (...) {
cout << "Default Exception\n";
}
return 0;
}
运行结果:
Default Exception
2.3隐式转换
对于原始类型,不会发生隐式转换的情况。
例如,下面代码中的'a'不会隐式转换为int (C++中‘a'代表一个字符,而C中是一个整数)。
#include <iostream>
using namespace std;
int main() {
try {
throw 'a';
}
catch (int x) {
cout << "Caught " << x;
}
catch (...) {
cout << "Default Exception\n";
}
return 0;
}
运行结果:
Default Exception
2.4没有异常捕获
如果抛出了异常,但是没有任何地方捕获,则程序会异常的终止。
例如,下面程序会终止。
#include <iostream>
using namespace std;
int main() {
try {
throw 'a';
}
catch (int x) {
cout << "Caught ";
}
return 0;
}
程序运行时,发生异常。
gcc4.8.5+CentOS7.2:
terminate called after throwing an instance of 'char'
Aborted
windows7 + visual studio2015:
abort() has been called
我们可以使用自己定义的异常函数来替换这个异常终止的行为。
2.5继承类中的捕获顺序
子类的异常应该在基类异常之前被捕获。具体可参考本博客的这篇文章《C++异常(3) - 捕获基类与子类的异常》。
2.6标准异常处理类
与Java类似, C++库中也定义了一个标准异常处理类,它是所有其它标准类的基类。
标准库中抛出的所有异常对象都是继承自这个类。因此,所有的标准异常都能使用这个类型来进行捕获。
2.7异常检查
不同于Java,C++中所有异常是不检查的。编译器不会检查一个异常是否被捕获。例如函数声明时不需要指出所有不会被捕获的异常,尽管推荐这么做。
例子:下面程序编译正常,但是严格来说fun()应该列出它不会检测的异常。
#include <iostream>
using namespace std;
// 此函数的实现,对于编译器没什么问题,但是不推荐这么做。
// 理论上,此函数应该列出所有不会捕获的异常,并且函数名应该为
// "void fun(int *ptr, int x) throw(int *, int)"。
void fun(int* ptr, int x) {
if (ptr == NULL)
throw ptr;
if (x == 0)
throw x;
}
int main() {
try {
fun(NULL, 0);
}
catch (...) {
cout << "Caught exception from fun()";
}
return 0;
}
运行结果:
Caught exception from fun()
2.8嵌套捕获
C++中try-catch代码块是可以嵌套的。另外,一个异常可以使用throw重新抛出。
#include <iostream>
using namespace std;
int main() {
try {
try {
throw 20;
}
catch (int n) {
cout << "Handle partially\n";
throw; // 再次抛出异常
}
}
catch (int n) {
cout << "Handle remaining\n";
}
return 0;
}
运行结果:
Handle partially
Handle remaining
函数中也可以使用throw重新抛出异常。也可以只部分处理,然后请求调用者来处理剩余部分。
2.9try代码块的析构
当抛出了一个异常时。try代码块中创建的所有对象都会被析构掉,在将控制权转移到catch代码块之前。
#include <iostream>
using namespace std;
class Test {
public:
Test() { cout << "Constructor of Test " << endl; }
~Test() { cout << "Destructor of Test " << endl; }
};
int main() {
try {
Test t1;
throw 10;
}
catch (int i) {
cout << "Caught " << i << endl;
}
}
运行结果:
Constructor of Test
Destructor of Test
Caught 10

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



