1 异常常识:
1)使用throw抛出异常;
2)使用try-catch 语句块捕获异常;
3)catch语句块中,可以使用throw再次抛出当前异常
try {
g();
} catch (int i) {
throw;//再次抛出异常
}
4)可以抛出任意类型的异常。可以是对象,也可以是简单的基础类型。
void process() {
if (fail) {
throw 5;
}
}
try {
process():
} catch (int e) {
return 1;
}
5)也可以抛出C风格的字符串char*
void process() {
if (fail) {
throw "failed exception"
}
}
try {
process():
} catch (const char* e) {
std:cerr << e << endl;
}
6)通常应该抛出对象。因为对象的类名称可以传递信息;此外异常可以存储信息,包括用于描述异常的字符串;
7) 以下为匹配所有异常的代码:
try {
} catch (...) {
}
try {
} catch (const invalid_argument& e) {
} catch (const runtime_error& e) {
} catch (...) {
}
8)如果程序抛出的异常没有被捕获,程序将终止。可以对main()函数使用try-catch结构以捕获所有没有被捕获的异常;
2 抛出列表
C++允许指定函数或者方法可以抛出的异常。也即抛出列表。
void readFile()
throw(invalid_argument, runtime_error)
{
//code
}
注意:
1)不能仅仅根据抛出列表中的不同异常重载函数;
2)如果函数/方法没有指定抛出列表,那么可以抛出任意异常。
3)如果不想让函数/方法抛出异常,可以使用noexcept
void readFile() noexcept;
4)函数/方法可以抛出 抛出列表之外的异常,但是会导致程序终止;
void readFile() throw(invalid_argument, runtime_error) {
throw 5;
}
int main() {
try {
readFile();
} catch (int x) {
}
return 0;
}
上面的代码会导致程序终止。
5)set_unexcepted
如果出现意料之外的异常,可以使用set_unexcepted更改其行为。
规则如下:
(1)如果处理函数抛出一个新的异常,这个新的异常将会替换掉意料之外的异常,就像新的异常是最初被抛出的一样。
(2)如果新抛出的异常也不在抛出列表中,程序处理如下:
(2.1)如果抛出列表给出了bad_exception,就抛出bad_exception;
(2.2)如果抛出列表没有给出bac_exception,就终止;
set_unexcepted通常用于将意料之外的异常转化为预期的异常;
void myUnexcepted() {
throw runtime_error("");
}
int main() {
unexcepted_handler old_handler = set_unexcepted(myUnexcepted);//保存旧的处理函数
try {
} catch (int e) {
} catch (const runtime_error& e) {
}
set_unexcepted(old_handler);//还原
}
由于unexcepted函数作用于整个引用程序而不是这个函数,所以当需要特殊处理程序的代码结束以后,需要还原处理程序。
3 在重写方法中修改抛出列表
在子类中重写虚方法时,如果想让抛出列表比父类中的抛出列表更加严格,可以修改抛出列表。
总结为允许三种情况:
1)删除列表中的异常(注意不是全部删除);
2)添加超类抛出列表中异常的子类;
3)将方法设置为noexcept;
代码例子:
class D {
};
class A {
};
class B : public A {
};
class C : public B {
};
class Foo {
public:
void virtual func() throw (A,D) = 0;
};
class Bar: public Foo {
public:
void virtual func() throw(A);//删除异常D,允许
void virtual func() throw(A,D,B);//添加A的子类,允许
void virtual func() throw(B,C);//允许
//void virtual func() noexcept;//允许
//void virtual func() throw(B, int);//不允许
//void virtual func();//全部删除,不允许
};
4 嵌套异常
using namespace std;
class MyException : public exception {
public:
MyException(const char* msg):mMsg("") {
mMsg = msg;
}
virtual ~MyException() noexcept {}
virtual const char* what() const noexcept override {
return mMsg.c_str();
}
private:
string mMsg;
};
void doSomething() {
try {
throw runtime_error("runtime_error");
} catch (const runtime_error& e) {
cout << "catch runtime_error exception : " << e.what() << endl;
cout <<" throw MyException :" << endl;
throw_with_nested(MyException("Myxception"));
}
}
int main(int argc, const char * argv[]) {
// insert code here...
try {
doSomething();
} catch (const MyException& e) {
const nested_exception *p = dynamic_cast(&e);
if (p) {
try {
p->rethrow_nested();
} catch (const runtime_error& e) {
cout << "nested exceprion => " << e.what() << endl;
}
}
}
}
执行结果:
catch runtime_error exception : runtime_error
throw MyException :
nested exceprion => runtime_error
备注:
1)C++高级编程中,讲到嵌套类必须要同时继承混入类std::nested_exception,但是实际测试,不继承也是可以的。
2) 上面使用dynamic_cast将MyException转型,然后获取嵌套类,可以使用rethrow_if_nested简化,代码如下:
int main(int argc, const char * argv[]) {
// insert code here...
try {
doSomething();
} catch (const MyException& e) {
try {
std::rethrow_if_nested(e);
} catch (const runtime_error& e1) {
cout << "nested exceprion => " << e1.what() << endl;
}
}
}