南阳网网站建设,企业手机网站建设策划方案,电商网站界面设计流程,wordpress 小工具制作本专栏目的
更新C/C的基础语法#xff0c;包括C的一些新特性
前言 通过前面几节课#xff0c;我们学习了抽象、封装、继承、多态等相关的概念#xff0c;接下来我们将讲解异常#xff0c;异常是专门处理错误的#xff1b;这一次加了不少图标#xff0c;希望大家喜欢;C语…本专栏目的
更新C/C的基础语法包括C的一些新特性
前言 通过前面几节课我们学习了抽象、封装、继承、多态等相关的概念接下来我们将讲解异常异常是专门处理错误的这一次加了不少图标希望大家喜欢;C语言后面也会继续更新知识点如内联汇编欢迎收藏 关注本人将会持续更新。 文章目录 :eyes: 先看问题:question: 什么是异常:arrow_upper_right: 抛出异常:cat: 捕获异常:star: C 标准的异常:new: 定义新的异常(有头文件):accept: 万能接收异常:stop_button: 抑制new抛异常:see_no_evil: 函数中捕获异常注意点:atom_symbol: 异常注意点 先看问题
我们先来看这一段代码
void _div(double a, double b)
{std::cout a / b std::endl;
}这段代码很简单就是实现一个除法运算但是这个代码有一个很大的bug就是没有判断b!0的情况所以很多人写代码的时候是这样写的
void _div(double a, double b)
{if(b 0) {std::cout 除数不能为0 std::endl;return;}std::cout a / b std::endl;
} 但是如果我们忘记了写判断而这一段代码在一个程序中无非就是一个小部分如果我们忘记写这个判断了程序就会引发中断而这个是逻辑错误不是代码错误编译器是不会提示的所以这个时候我们就需要一个一个去找bug的位置了那有什么更好的解决方法呢
当然有这就是异常。
❓ 什么是异常
异常是程序在执行期间产生的问题也就是指在程序运行时发生的特殊情况比如我们上面的尝试除以零的操作。 为什么异常可以这样解决程序中产生的问题呢
概念异常处理提供了一种可以使程序从执行的某点将控制流和信息转移到与执行先前经过的某点相关联的处理代码的方法换言之异常处理就是将控制权沿调用栈向上转移。 关键
将程序运行的的PC指针沿着栈向上移动换句话说就是将程序出现异常的时候那个位置移动到另外一个位置。 4️⃣ C处理异常API
C 异常处理涉及到四个关键字**try、catch、throw、noexcept **。 throw:程序会抛出一个异常。 catch: 通过异常处理程序捕获异常。 try: try中存放是否需要检查有异常的的代码段它后面通常跟着一个或多个 catch 块。 try
{// 保护代码
}catch( ExceptionName e1 ) // ExceptionName 异常类型名字
{// catch 块
}catch( ExceptionName e2 )
{// catch 块
}catch( ExceptionName eN )
{// catch 块
}**noexcept **用于描述函数不会抛出异常一旦有异常抛出会立刻终止程序它可以阻止异常的传播与扩散 扩展noexcept可以带一个“常量表达式作为参数常量表达式为true表示不会抛出异常否则代表可以抛出异常如noexpect(true) (也是默认的)noexpect(false) int show() noexpect // 声明这个函数不会抛出异常
{throw 0;
}int show() noexpect(false) // 声明这个函数*** 会 *** 抛出异常
{throw 0;
}↗️ 抛出异常
我们上面可以知道跑出异常的关键字是throw那具体怎么使用呢我们这里以解决我们上面的案例为例❗️❗️
double _div(double a, double b)
{if( b 0 ) // 除数为0错误抛出异常{throw Division by zero condition!; }return (a / b);
}捕获异常
catch 块跟在 try 块后面用于捕获异常。我们可以指定想要捕捉的异常类型通过指定 catch 关键字即可。
double _div(double a, double b)
{try{if (b 0) // 除数为0错误抛出异常{throw std::runtime_error(Division by zero condition!);}return (a / b);}catch (std::exception err) {std::cout err.what() std::endl;}
}上面的代码会捕获一个类型为 std::runtime_error的异常catch存放处理异常方法这样就不会发送中断了运如图 抛出异常代码模板结构大概如下
try
{// 保护代码
}catch(...)
{// 能处理任何异常的代码
}当然catch还可以多个多次匹配不同的问题如下代码
double _div(double a, double b)
{try{if (b 0) // 除数为0错误抛出异常{throw 0;}return (a / b);}catch (const char* err) {std::cout err std::endl;}catch (int err) {std::cout 1 std::endl;}
}运行结果 由于我们抛出了一个类型为 int 的异常在捕获异常的时候他会匹配不同抛出异常的类型我们抛出的是整形
所以C会自动寻找相对应位置。
❓ 如果throw的类型在catch中没有找到会怎么样子呢这个就留给读者思考吧
⭐️ C 标准的异常
C 提供了一系列标准的异常定义在 中这些异常是我们程序中容易犯错的结果我们可以在程序中使用这些标准的异常异常家族结构图如下(一部分) 下表是对上面层次结构中出现的每个异常的说明
异常描述std::exception该异常是所有标准 C 异常的父类。std::bad_alloc该异常可以通过 new 抛出。std::bad_cast该异常可以通过 dynamic_cast 抛出。std::bad_exception这在处理 C 程序中无法预期的异常时非常有用。std::bad_typeid该异常可以通过 typeid 抛出。std::logic_error理论上可以通过读取代码来检测到的异常。std::domain_error当使用了一个无效的数学域时会抛出该异常。std::invalid_argument当使用了无效的参数时会抛出该异常。std::length_error当创建了太长的 std::string 时会抛出该异常。std::out_of_range该异常可以通过方法抛出例如 std::vector 和 std::bitset::operator。std::runtime_error用于表示那些在程序运行时可能发生的错误.std::overflow_error当发生数学上溢时会抛出该异常。std::range_error当尝试存储超出范围的值时会抛出该异常。std::underflow_error当发生数学下溢时会抛出该异常。 结合我们之前学过的面向对象封装、继承、多态我们可以发现这些异常都是继承exception类所以对于这些标准异常的捕获处理我们就可以得到如下代码结构
try {throw 标准库中含有的异常;
}
catch(std::exception err){} 定义新的异常(有头文件)
您可以通过继承和重载 exception 类来定义新的异常。下面的实例演示了如何使用 std::exception 类来实现自己的异常
#include iostream
#include exception
using namespace std;struct MyException : public exception //exception 在std命名空间里面
{const char * what () const{return C Exception;}
};int main()
{try{throw MyException();}catch(MyException e){std::cout MyException caught std::endl;std::cout e.what() std::endl;}catch(std::exception e){//其他的错误}
}这将产生以下结果
MyException caught
C Exception万能接收异常
C考虑的很全面提供了一个能够捕获万能捕获异常的方法...代码结构如下
try {throw hello;
} catch (...) {}我们将案例用这个方法捕获
double _div(double a, double b)
{try{if (b 0) // 除数为0错误抛出异常{throw 0;}return (a / b);}catch (...) {std::cout __FUNCTION__ 代码有问题 std::endl;}
}结果 ⏹ 抑制new抛异常
当使用new申请内存时如果内存申请失败会抛出std::bad_alloc异常如果像让他不发生异常则需要如下处理学习github某一位老师的笔记。
测试的时候需要换成 x86 环境下2gx64 理论上无限内存
try
{while (true){new char[1024];}
}
catch (const std::bad_alloc e)
{cout has exception e.what() endl;
}如果想根据返回的指针来判断就需要抑制new抛出异常。
double* p nullptr;
do
{p new(std::nothrow) double[1024]; //声明让 new 不抛出异常
} while (p);函数中捕获异常注意点
当在函数中没有匹配处理该抛出异常的操作这个时候会他会到函数调用的地方去寻找匹配如下
double _div(double a, double b)
{try{if (b 0) // 除数为0错误抛出异常, *** 但是没有匹配 0 的代码 ***{throw 0;}return (a / b);}catch (const char* msg) { // 匹配字符串类型的std::cout __FUNCTION__ 代码有问题 std::endl;}
}// 这个时候回到函数调用的地方找寻
int main()
{try {_div(10, 0);}catch (...) { // 这个地方找std::cout main std::endl;}}运行结果图如下 ⚛️ 异常注意点 类的构造函数不抛出异常 异常不能乱用C一般用的不多不像java那样动不动就抛一个异常以下是使用异常的一些标准 如果程序是逻辑错误则不应该抛出异常应该解决他 如果后面的代码依赖这个结果那么这个有异常情况则可以抛出异常 如果后面的代码不依赖这个结果则不应该抛出异常 异常不能用if……else代替