基本的异常处理
异常处理机制:暂缓问题处理,不在当前函数中处理,在他的调用者中处理(先上车,后补票)
什么是异常:任何东西都可以认为是异常,错误只是异常的一种
异常一旦被抛出,不做处理,如果引发异常,会调用默认abort函数终止程序
捕获和处理异常:
throw 抛出异常: (值是任何类型都可以,只是我们处理异常的一个参照,类似返回值)
try(检查,捕获)和catch(处理异常): 必须是一起出现,并且它们的括号{ }不能省略
tip: 任何东西都可以认为是异常,错误只是异常中的一种---出现一种情况不能让程序正常运行
怎么抛出异常
//求a和b的余数 怎么抛出异常
int division(int a,int b){
if(b==0)
throw 0; //抛出一个值(任意)--->之后处理
return a/b;
}
void print(int a,int b){
cout<<division(a,b);
}
int main(){
print(1,0);
}
异常一旦被抛出,不做处理,如果引发异常,会调用默认abort函数终止程序
捕获和处理异常
//try 与catch必须是一起出现,并且他们的括号{}不能省略
try
{
//正常需要检查是否存在异常的代码
}
catch(类型) //理解为switch中case语句
{
//处理是根据抛出数据类型决定如何处理 匹配项 匹配抛出异常的类型
}
//一个try可以对应多个catch
try
{
//...
}
catch(int)
{
}
catch(double)
{
}
catch(string)
{
}
//catch和if else_if 执行机制是一样的,只能执行一个匹配项
小知识:
- 对try{ } catch(){ } 的理解:在当前位置引发异常,直接从这个位置跳到catch的位置执行catch的代码 --- 类似switch case 语句
- catch中int和char不会做转换
- 写两个相同的类型不被允许,哪段代码先引发异常就先调用catch
int division(int a,int b){
if(b==0)
throw 0;
return a/b;
}
void print(int a,int b){
cout<<division(a,b);
}
int main(){
try
{
print(1,0); //检查异常
cout<<"别的代码"<<endl; //这一句不会运行,会直接跳到catch
}
catch(int) //抛出的是int类型,捕获int类型
{
cout<<"除数不能为0"<<endl;
}
}
程序能抛出(存在)多个异常,但是只能同时处理1个异常,不能同时引发多个异常
不存在异常的描述 --- 标识性作用
- throw ()
- noexcept
//某个函数不存在异常,在某个函数后面用throw() 描述,表示它不存在异常
void print() throw()
{
cout << "当前函数不存在抛出异常操作" << endl;
}
void printData() noexcept
{
cout << "c++新标准中的关键字: 不存在抛出异常操作" << endl;
//throw 0; 一旦说明没有异常操作,就不能抛出
}
删减符 ...
任何类型的异常都捕获 不管抛出啥,在哪里抛出的,只要引发异常都可以捕获到
catch(...)
{
cout <<"捕获任何类型的异常"<< endl;
}
异常处理中的传参操作 --- 可以写一个变量进去
catch(int a)
注意c++中string的处理
代码解析:
对 通过抛出字符串,隐藏了一个传参操作 的理解
int divisor(int a, int b)
{
if (b == 0)
throw string("除数不能为0");
return a / b;
}
int main()
{
try
{
divisor(1, 0);
}
catch (string str) //把throw的内容赋值给str str="除数不能为0"
{
cout << str << endl;
}
}
注意string类型与const char* 类型区别 --- 出现类型不匹配,c++对传参类型要求更严格
int divisor(int a, int b)
{
if (b == 0)
throw "除数不能为0"; //抛出异常 解析为char* 类型 写catch时不能直接当作string
if(b==1)
throw "除数不能为1";
if(b==2)
throw string("除数不能为2"); //需构造无名参数作捕获对象处理--->需要自己触发
return a / b;
}
int main()
{
try
{
divisor(1, 0); //直接触发异常
}
catch (const char* str) //抛出的是char* 类型,不能当作string
{
cout << str << endl;
}
try
{
divisor(1, 2);
}
catch (string str) //如果要捕获string类型,需要自己构造一个string对象返回
{
cout << str << endl; //直接输出str
}
}
可以抛出自己类的对象
class Error
{
public:
Error(const char* str = "未知错误") :str(str) {}
const char* what()const
{
return str.c_str();
}
protected:
string str;
};
void insertArray(int array[], int* curNum, int posData,int maxLength)
{
if (*curNum >= maxLength) //3>=3
{
throw Error("数组下标溢出!");
}
//0 1 2
array[*curNum] = posData; //array[3]=3
(*curNum)++;
}
int main(){
try
{
int array[3] = { 0,0,0 };
int curNum = 0;
for (int i = 0; i < 4; i++)
{
insertArray(array, &curNum, i, 3);
}
}
catch (Error str)
{
cout << str.what() << endl;
}
return 0;
}
标准库当中的异常类
#include<exception> //父类(基类)
子类很多,子类描述的问题不同而已
例子: const char* _ptr; 一个数据成员,用于描述标准库当中异常的字符串,用字符指针存放那个字符串
what( )方法 用于返回数据成员的 1.虚函数 2.不存在异常
return _ptr ? _ptr : "unknow";判断char* 类型的指针是不是为空,不等于空,返回你描述的错误,等于空(由于没有传参),返回未知错误"unknow"
引发标准库中内存申请失败的异常
发现代码出现abort( )错误,可以通过这种方式找到,这里是针对内存申请失败做了单一处理,如果不做处理,会直接调用abort函数终止程序
#include <exception>
#include <iostream>
using namespace std;
class Exception
{
public:
Exception(const char* ptr="UNKNOW") :ptr(const_cast<char*>(ptr)){}
virtual const char* what() const //父类是虚函数 且不存在异常
{
return ptr;
}
protected:
char* ptr;
};
//子类继承父类
class Bad_alloc :public Exception
{
public:
Bad_alloc(const char* _Message = "bad exception") :Exception(_Message) {}
protected:
};
//子类继承父类 调用父类构造函数
class Run_time :public Exception
{
public:
Run_time(const char* _Message = "run_time error") :Exception(_Message) {}
protected:
};
int main()
{
try
{
while (1)
{
int* p = new int[1024*1024*10];//一直做内存申请,不做释放,最后一定会内存申请失败
}
}
catch (bad_alloc& object)
{
cout << object.what() << endl;
}
return 0;
}
bad allocation //调用时抛出 bad allocation 是子类对象调用继承下来的what()方法
//一般写代码出现莫名的中断,原因是不做异常处理,引发了abort函数中断程序,一般这种错误都是特殊错误
标准库中传两个参数起到标识作用,由于:引发了不同的错误,不同错误对应了不同的错误编码 对这些错误有特定的描述 ---> 工具 ---> 错误查找 ---> 输入错误编码
值:3
错误信息:系统找不到指定的路径
以上就是深入了解C++异常处理的详细内容,更多关于C++异常处理的资料请关注编程网其它相关文章!