一、异常与继承
如果异常类型为C++的类,并且该类有其基类,则应该将派生类的错误处理程序放在前面,基类的错误处理程序放在后面
#include <iostream>
#include <string>
using namespace std;
class MyException
{
public:
MyException(const char *message)
: message_(message)
{
cout << "MyException ..." << endl;
}
MyException(const MyException &other) : message_(other.message_)
{
cout << "Copy MyException ..." << endl;
}
virtual ~MyException()
{
cout << "~MyException ..." << endl;
}
const char *what() const
{
return message_.c_str();
}
private:
string message_;
};
class MyExceptionD : public MyException
{
public:
MyExceptionD(const char *message)
: MyException(message)
{
cout << "MyExceptionD ..." << endl;
}
MyExceptionD(const MyExceptionD &other)
: MyException(other)
{
cout << "Copy MyExceptionD ..." << endl;
}
~MyExceptionD()
{
cout << "~MyExceptionD ..." << endl;
}
};
int main(void)
{
try
{
MyExceptionD e("test exception");
throw e;
}
catch (MyExceptionD &e)
{
cout << "catch MyExceptionD ..." << endl;
cout << e.what() << endl;
}
catch (MyException &e)
{
cout << "catch MyException ..." << endl;
cout << e.what() << endl;
}
return 0;
}
派生类的异常能够被基类所捕获,且前面的异常处理程序能够匹配的话首先catch,如果把基类的放在最前面,而且不是引用的形式,如 catch (MyException e); 那么将会被这个所catch 到,而且在构造e 的过程会有object slicing 的问题。
二、异常与指针
抛出指针通常是一个坏主意,因为抛出指针要求在对应处理代码存在的任意地方都存在指针所指向的对象(注意此时throw抛出时复制的是指针本身,不会去复制指针指向的内容)
int main(void)
{
try
{
//MyExceptionD e("test exception");
//throw &e;
throw new MyExceptionD("test exception");
}
/*catch (void* e)
{
cout<<"catch void* ..."<<endl;
cout<<((MyExceptionD*)e)->what()<<endl;
delete (MyExceptionD*)e;
}*/
catch (MyExceptionD *e)
{
cout << "catch MyExceptionD ..." << endl;
cout << e->what() << endl;
delete e;
}
catch (MyException &e)
{
cout << "catch MyException ..." << endl;
cout << e.what() << endl;
}
return 0;
}
其中MyException, MyExeptionD类如上所示,现在MyExeptionD 对象是在堆上分配的,所以在catch 的时候还没释放,还可以访问到e->what(); 但需要自己在catch 末尾delete e; 假设将 throw new MyExceptionD("test exception"); 换成MyExceptionD e("test exception");throw &e;
即抛出局部对象的指针,由于在catch 时MyExeptionD 对象已经被析构了,所以访问不到e->what(); 即e是空悬指针。
还有一点是,任何类型的指针都能被void* 指针所捕获,如果将catch (void* e);注释打开,那么由于排在前面,异常首先将被它所捕获。
三、异常规格说明
1、异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的声明中列出这个函数可能抛掷的所有异常类型。 例如:
void fun() throw(A,B,C,D);
2、若无异常接口声明,则此函数可以抛掷任何类型的异常。
3、不抛掷任何类型异常的函数声明如下: void fun() throw();
void fun(int n) throw (int, MyException, MyExceptionD)
{
if (n == 1)
{
throw 1;
}
else if (n == 2)
{
throw MyException("test Exception");
}
else if (n == 3)
{
throw MyExceptionD("test ExceptionD");
}
}
void fun2() throw()
{
}
int main(void)
{
try
{
fun(2);
}
catch (int n)
{
cout << "catch int ..." << endl;
cout << "n=" << n << endl;
}
catch (MyExceptionD &e)
{
cout << "catch MyExceptionD ..." << endl;
cout << e.what() << endl;
}
catch (MyException &e)
{
cout << "catch MyException ..." << endl;
cout << e.what() << endl;
}
return 0;
}
实际上编译会产生警告:
warning C4290: 忽略 C++ 异常规范,但指示函数不是 __declspec(nothrow)
就是说VC++编译器现在还不怎么支持异常规格说明,举个例子说,void fun(int n) throw (int, MyException, MyExceptionD); 没有声明double 类型的异常,但在函数内throw 1.0; 在外部catch (double) 还是会成功。
四、C++标准库异常层次
比如dynamic_cast 执行错误会产生bad_cast 异常,new 分配内存错误会产生bad_alloc 异常,其实这些异常类都继承自exception类,但内部的实现都
没有有效的代码,只是用来标识当前程序产生了哪种类型的异常而已。
参考:
C++ primer 第四版 Effective C++ 3rd C++编程规范