C++ 异常处理涉及到三个关键字:try、catch、throw。 在 c++程序中,任何需要检测异常的语句,都必须在 try 语句块中执行,异常必须由紧跟着 try 语句后面的 catch 语句来捕获并处理,因此 try 与 catch 总是结合使用,下面是它们的语法
throw 异常类型;try
{//try 语句块
}
catch(类型 n 参数 n)
{//异常处理语句
}
throw 是用来抛出异常的,它抛出的异常类型可以是普通类型也可以是聚合类型。
• 基本类型:int、char、float
• 聚合类型:指针、数组、字符串、结构体
例如:
int init()
{int fd=open("1.txt",O_RDWR);if(fd==-1){throw "打开失败"; //抛出字符串类型异常}char buf[128];int rd=read(fd,buf,128);if(rd==-1){throw 1.88; //抛出浮点型类型异常}return 1;
}
可以看到当程序出错时,就可以使用 throw 抛出不同异常类型,用以进行不同错误处理。
try 是用来检测抛出的错误的,例如:
try
{//下面两个函数可能会抛出异常int a = init();copyDir();
}
通常,把可能会抛出异常的函数对象都包含在 try 中做检测。
而 catch 就是用来对抛出的错误进行判断和处理的,它必须在 try 的后面。
try
{int a=init();cout<<a<<endl;
}
catch(const char* na)
{cout<<na<<endl;
}
catch(float b)
{cout<<b<<endl;
}
catch(...)
{cout<<"其他情况"<<endl;
}
catch 会判断 try 检测到的异常的类型,根据类型开发者可以对其进行不同的处理(备注:C++
中…在 catch 中的作用相当于 default)
throw 的异常规范写法
throw 关键字除了可以用在函数体中抛出异常,还可以用在函数头和函数体之间,指明当前函数能够抛出的异常类型,这称为异常规范(Exception specification)
第一种:例如:
double func ( char param ) throw ( int );
声明了一个名为 func 的函数,它的返回值类型为 double,有一个 char 类型的参数,并且只能抛出 int 类型的异常。如果抛出其他类型的异常,try 将无法捕获,只能终止程序。
第二种:例如:
double func ( char param ) throw ( int , char , exception );
函数可抛出多种类型的异常,用逗号隔开
第三种:例如:
double func ( char param ) throw ();
如果函数不会抛出任何异常,那么( )中什么也不写,如此,func() 函数就不能抛出任何类型的异常了,即使抛出了,try 也检测不到。
但是异常规范是 C++98 新增的一项功能,但是后来的 C++11 已经将它抛弃了,不再建
议使用,嘻嘻。
exception 异常类
C++ 语言本身以及标准库中的函数抛出的异常,都是 exception 类或其子类的异常。也就是说,抛出异常时,会创建一个 exception 类或其子类的对象。 之所以使用引用,是为了提高效率。如果不使用引用,就要经历一次对象拷贝(要调用拷贝构造函数)的过程。
上面就是 std 中包含的一些异常类型,它们都是继承 exception 而来的,例如当使用了无效的参数时,就会抛出一个 std::invalid_argument 的错误,而程序开发者其实要定义新的异常类型时,可以继承 exception 类来做处理。
上图是系统中 bad_alloc 异常类型的由来,可以看到他也是继承 exception 而来的,由此示例,当我们想要其他类型的异常处理的时,我们也可以根据上图的方法去继承一个新异常类型,例
如:
#include <iostream>
using namespace std;struct MyException : public exception
{const char* what() const throw (){return "C++ Exception";}
};int main()
{try{throw MyException();}catch (MyException & e){std::cout << e.what() << std::endl;}catch (std::exception & e){//其他的错误}return 0;
}
MyException 就是我们新创建的一个异常类型,当遇到一些相应的错误时,我们就可以返回这个异常类型,然后在这个异常类型的成员函数 what 中写入我们的错误处理提示。
异常层次
同一个 try 语句块可以对应多个 catch 语句块,catch 语句块可以定义具体处理的异常类型,不同的类型的异常由不同的 catch 语句块处理;
try 语句块可以抛出任何类型的异常,catch(...)用于处理所有类型的异常,任何异常都只能被捕获一次;
throw 抛出的异常必须被 catch 处理,如果当前函数能够处理异常,继续执行;如果当前函 数不能处理异常,函数停止执行并返回;
未被处理的异常会顺着函数调用栈向上传递,直到被处理为止,否则程序将停止执行。
noexcept 关键字
在上面说道在 C++11 之前,表示函数是否会抛出异常是通过在函数后面加入 throw 来说明的,具体查看 上面的异常规范,但是在 C++11 之后,就一般使用 noexcept 关键字来修饰函数是否会抛出异常了。
int close_t ( int s ) noexcept ;
例如上面表名 close_t 函数绝对不会抛出异常,这是第一种形式,就是直接在函数后面加入
noexcept 关键字来表名函数绝对不会抛出异常,接下来看第二种。
int close_t ( int x , int y ) noexcept ( 1 > 0 ){return 0 ;}
在 noexcept 后面可以加上一个常量表达式,表达式的值只能是 true 或 false,当为 true 时其作用就和直接写 noexcept 的效果是一样的,表明不会抛出异常,当为 false 的时候表明可能会抛出
异常。
最常用的就是就是用 noexcept 当运算符配合第二种方式进行,例如:
int fun()
{return 1;
}
int close_t(int x,int y) noexcept(noexcept(fun()))
{return 0;
}
上面 fun()函数如果不抛出异常那么 noexcept(fun())就会返回 true,就相当于表明 close_t 也 不会抛出异常,使得两个函数的异常抛出情况相同。
或:
int close_t(int x,int y) noexcept(noexcept(x>y))
{return 0;
}
上面则是常用的根据函数参数情况来指定函数是否会抛出异常。