纯净、安全、绿色的下载网站
开发程序是一项“烧脑”的工作程序员不但要经过长期的知识学习和思维训练还要做到一丝不苟注意每一个细节和边界。即使这样也不能防止程序出错。
专家指出长期作息不规律 + 用脑过度的危害很大可能会诱发神经衰弱、失眠等疾病。我就是受害者之一曾被失眠困扰了好几年不但入睡困难还容易早醒。程序员要注意劳逸结合多去健身房多跑步多打球多陪女朋友旅游等千万不要熬夜以为深夜写代码效率高这样会透支年轻的身体。
程序的错误大致可以分为三种分别是语法错误、逻辑错误和运行时错误:
1) 语法错误在编译和链接阶段就能发现只有 100% 符合语法规则的代码才能生成可执行程序。语法错误是最容易发现、最容易定位、最容易排除的错误程序员最不需要担心的就是这种错误。
2) 逻辑错误是说我们编写的代码思路有问题不能够达到最终的目标这种错误可以通过调试来解决。
3) 运行时错误是指程序在运行期间发生的错误例如除数为 0、内存分配失败、数组越界、文件不存在等。C++ 异常(Exception)机制就是为解决运行时错误而引入的。
运行时错误如果放任不管系统就会执行默认的操作终止程序运行也就是我们常说的程序崩溃(Crash)。C++ 提供了异常(Exception)机制让我们能够捕获运行时错误给程序一次“起死回生”的机会或者至少告诉用户发生了什么再终止程序。
【例1】一个发生运行时错误的程序:
#include <iostream> #include <string> using namespace std; int main(){ string str = "http://c.biancheng.net"; char ch1 = str[100]; //下标越界ch1为垃圾值 cout<<ch1<<endl; char ch2 = str.at(100); //下标越界抛出异常 cout<<ch2<<endl; return 0; }
运行代码在控制台输出 ch1 的值后程序崩溃。下面我们来分析一下原因。
at() 是 string 类的一个成员函数它会根据下标来返回字符串的一个字符。与[ ]不同at() 会检查下标是否越界如果越界就抛出一个异常;而[ ]不做检查不管下标是多少都会照常访问。
所谓抛出异常就是报告一个运行时错误程序员可以根据错误信息来进一步处理。
上面的代码中下标 100 显然超出了字符串 str 的长度。由于第 6 行代码不会检查下标越界虽然有逻辑错误但是程序能够正常运行。而第 8 行代码则不同at() 函数检测到下标越界会抛出一个异常这个异常可以由程序员处理但是我们在代码中并没有处理所以系统只能执行默认的操作也即终止程序执行。
我们可以借助 C++ 异常机制来捕获上面的异常避免程序崩溃。捕获异常的语法为:
try{ // 可能抛出异常的语句 }catch(exceptionType variable){ // 处理异常的语句 }
try和catch都是 C++ 中的关键字后跟语句块不能省略{ }。try 中包含可能会抛出异常的语句一旦有异常抛出就会被后面的 catch 捕获。从 try 的意思可以看出它只是“检测”语句块有没有异常如果没有发生异常它就“检测”不到。catch 是“抓住”的意思用来捕获并处理 try 检测到的异常;如果 try 语句块没有检测到异常(没有异常抛出)那么就不会执行 catch 中的语句。
这就好比catch 告诉 try:你去检测一下程序有没有错误有错误的话就告诉我我来处理没有的话就不要理我!
catch 关键字后面的exceptionType variable指明了当前 catch 可以处理的异常类型以及具体的出错信息。我们稍后再对异常类型展开讲解当务之急是演示一下 try-catch 的用法先让读者有一个整体上的认识。
【例2】修改上面的代码加入捕获异常的语句:
#include <iostream> #include <string> #include <exception> using namespace std; int main(){ string str = "http://c.biancheng.net"; try{ char ch1 = str[100]; cout<<ch1<<endl; }catch(exception e){ cout<<"[1]out of bound!"<<endl; } try{ char ch2 = str.at(100); cout<<ch2<<endl; }catch(exception &e){ //exception类位于<exception>头文件中 cout<<"[2]out of bound!"<<endl; } return 0; }
运行结果:
(
[2]out of bound!
可以看出第一个 try 没有捕获到异常输出了一个没有意义的字符(垃圾值)。因为[ ]不会检查下标越界不会抛出异常所以即使有错误try 也检测不到。换句话说发生异常时必须将异常明确地抛出try 才能检测到;如果不抛出来即使有异常 try 也检测不到。所谓抛出异常就是明确地告诉程序发生了什么错误。
第二个 try 检测到了异常并交给 catch 处理执行 catch 中的语句。需要说明的是异常一旦抛出会立刻被 try 检测到并且不会再执行异常点(异常发生位置)后面的语句。本例中抛出异常的位置是第 17 行的 at() 函数它后面的 cout 语句就不会再被执行所以看不到它的输出。
说得直接一点检测到异常后程序的执行流会发生跳转从异常点跳转到 catch 所在的位置位于异常点之后的、并且在当前 try 块内的语句就都不会再执行了;即使 catch 语句成功地处理了错误程序的执行流也不会再回退到异常点所以这些语句永远都没有执行的机会了。本例中第 18 行代码就是被跳过的代码。
执行完 catch 块所包含的代码后程序会继续执行 catch 块后面的代码就恢复了正常的执行流。
为了演示「不明确地抛出异常就检测不到异常」大家不妨将第 10 行代码改为char ch1 = str[100000000];访问第 100 个字符可能不会发生异常但是访问第 1 亿个字符肯定会发生异常了这个异常就是内存访问错误。运行更改后的程序会发现第 10 行代码产生了异常导致程序崩溃了这说明 try-catch 并没有捕获到这个异常。
关于「如何抛出异常」我们将在下节讲解这里重点是让大家明白异常的处理流程:
抛出(Throw)--> 检测(Try) --> 捕获(Catch)
异常可以发生在当前的 try 块中也可以发生在 try 块所调用的某个函数中或者是所调用的函数又调用了另外的一个函数这个另外的函数中发生了异常。这些异常都可以被 try 检测到。
1) 下面的例子演示了 try 块中直接发生的异常:
#include <iostream> #include <string> #include <exception> using namespace std; int main(){ try{ throw "Unknown Exception"; //抛出异常 cout<<"This statement will not be executed."<<endl; }catch(const char* &e){ cout<<e<<endl; } return 0; }
运行结果:
Unknown Exception
throw关键字用来抛出一个异常这个异常会被 try 检测到进而被 catch 捕获。关于 throw 的用法我们将在下节深入讲解这里大家只需要知道在 try 块中直接抛出的异常会被 try 检测到。
2) 下面的例子演示了 try 块中调用的某个函数中发生了异常:
#include <iostream> #include <string> #include <exception> using namespace std; void func(){ throw "Unknown Exception"; //抛出异常 cout<<"[1]This statement will not be executed."<<endl; } int main(){ try{ func(); cout<<"[2]This statement will not be executed."<<endl; }catch(const char* &e){ cout<<e<<endl; } return 0; }
运行结果:
Unknown Exception
func() 在 try 块中被调用它抛出的异常会被 try 检测到进而被 catch 捕获。从运行结果可以看出func() 中的 cout 和 try 中的 cout 都没有被执行。
3) try 块中调用了某个函数该函数又调用了另外的一个函数这个另外的函数抛出了异常:
#include <iostream> #include <string> #include <exception> using namespace std; void func_inner(){ throw "Unknown Exception"; //抛出异常 cout<<"[1]This statement will not be executed."<<endl; } void func_outer(){ func_inner(); cout<<"[2]This statement will not be executed."<<endl; } int main(){ try{ func_outer(); cout<<"[3]This statement will not be executed."<<endl; }catch(const char* &e){ cout<<e<<endl; } return 0; }
运行结果:
Unknown Exception
发生异常后程序的执行流会沿着函数的调用链往前回退直到遇见 try 才停止。在这个回退过程中调用链中剩下的代码(所有函数中未被执行的代码)都会被跳过没有执行的机会了。
启动异常堆栈信息打印 SpringBoot之自定义启动异常堆栈信息打印方式
el-radio回显无法选择 element编辑表单el-radio回显之后无法选择的问题解决