程序的错误大致可以分为三种,分别是语法错误、逻辑错误和运行时错误:
1) 语法错误在编译和链接阶段就能发现,只有 100% 符合语法规则的代码才能生成可执行程序。语法错误是最容易发现、最容易定位、最容易排除的错误,程序员最不需要担心的就是这种错误。
2) 逻辑错误是说我们编写的代码思路有问题,不能够达到最终的目标,这种错误可以通过调试来解决。
3) 运行时错误是指程序在运行期间发生的错误,例如除数为 0、内存分配失败、数组越界、文件不存在等。C++ 异常(Exception)机制就是为解决运行时错误而引入的。
 
运行时错误如果放任不管,系统就会执行默认的操作,终止程序运行,也就是我们常说的程序崩溃(Crash)。
C++ 提供了异常(Exception)机制,让我们能够捕获运行时错误,给程序一次“起死回生”的机会,或者至少告诉用户发生了什么再终止程序。
捕获异常
我们可以借助 C++ 异常机制来捕获上面的异常,避免程序崩溃。捕获异常的语法为:
try{
    // 可能抛出异常的语句
}catch(exceptionType variable){
    // 处理异常的语句
}
这就好比,catch 告诉 try:你去检测一下程序有没有错误,有错误的话就告诉我,我来处理,没有就pass!
#include<iostream>
#include<exception>
 
using namespace std;
 
int main(int argc, char *argv[])
{
    try {
        char ch = str[100];
        cout<<ch<<endl;
    } catch (exception &e) {
        cout<<"ch out of bound"<<endl;
    }
 
    try {
        char ch1 = str.at(100);
        cout<<ch1<<endl;
    } catch (exception &e) {
        cout<<"ch1 out of bound"<<endl;
    }
    return 0;
}
运行结果:
 
ch1 out of bound
 
可以看出,第一个 try 没有捕获到异常,输出了一个没有意义的字符(垃圾值)。因为[ ]不会检查下标越界,不会抛出异常,所以即使有错误,try 也检测不到。
换句话说,发生异常时必须将异常明确地抛出,try 才能检测到;如果不抛出来,即使有异常 try 也检测不到。所谓抛出异常,就是明确地告诉程序发生了什么错误。
第二个 try 检测到了异常,并交给 catch 处理,执行 catch 中的语句。需要说明的是,异常一旦抛出,会立刻被 try 检测到,并且不会再执行异常点(异常发生位置)后面的语句。本例中抛出异常的位置是第 17 行的 at() 函数,它后面的 cout 语句就不会再被执行,所以看不到它的输出。
说得直接一点,检测到异常后程序的执行流会发生跳转,从异常点跳转到 catch 所在的位置,位于异常点之后的、并且在当前 try 块内的语句就都不会再执行了;即使 catch 语句成功地处理了错误,程序的执行流也不会再回退到异常点,所以这些语句永远都没有执行的机会了。本例中,第 18 行代码就是被跳过的代码。
执行完 catch 块所包含的代码后,程序会继续执行 catch 块后面的代码,就恢复了正常的执行流。
 
抛出(Throw)--> 检测(Try) --> 捕获(Catch)
try{
    throw "Unknown Exception";  //抛出异常
    cout<<"This statement will not be executed."<<endl;
}catch(const char* &e){
    cout<<e<<endl;
}
 
多级 catch
当异常发生时,程序会按照从上到下的顺序,将异常类型和 catch 所能接收的类型逐个匹配。一旦找到类型匹配的 catch 就停止检索,并将异常交给当前的 catch 处理(其他的 catch 不会被执行)。如果最终也没有找到匹配的 catch,就只能交给系统处理,终止程序的运行。
#include<iostream>
#include<exception>
using namespace std;
class Base{ };
class Derived: public Base{ };
 
int main(int argc, char *argv[])
{
    try{
        throw Derived();  //抛出自己的异常类型,实际上是创建一个Derived类型的匿名对象
        cout<<"This statement will not be executed."<<endl;
    }catch(int){
        cout<<"Exception type: int"<<endl;
    }catch(char *){
        cout<<"Exception type: cahr *"<<endl;
    }catch(Base){  //匹配成功(向上转型)
        cout<<"Exception type: Base"<<endl;
    }catch(Derived){
        cout<<"Exception type: Derived"<<endl;
    }
 
    return 0;
}
输出结果:
Exception type: Base
 
catch 在匹配过程中的类型转换
C/C++ 中存在多种多样的类型转换,以普通函数(非模板函数)为例,发生函数调用时,如果实参和形参的类型不是严格匹配,那么会将实参的类型进行适当的转换,以适应形参的类型,这些转换包括:
算数转换:例如 int 转换为 float,char 转换为 int,double 转换为 int 等。
向上转型:也就是派生类向基类的转换,请查看《C++向上转型(将派生类赋值给基类)》了解详情。
const 转换:也即将非 const 类型转换为 const 类型,例如将 char * 转换为 const char *。
数组或函数指针转换:如果函数形参不是引用类型,那么数组名会转换为数组指针,函数名也会转换为函数指针。
用户自定的类型转换。
 
#include <iostream>
using namespace std;
int main(){
    int nums[] = {1, 2, 3};
    try{
        throw nums;
        cout<<"This statement will not be executed."<<endl;
    }catch(const int *){
        cout<<"Exception type: const int *"<<endl;
    }
 
    return 0;
}
运行结果:
Exception type: const int *
nums 本来的类型是int [3],但是 catch 中没有严格匹配的类型,所以先转换为int *,再转换为const int *。
 
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 也检测不到。
int disp() throw(int, int)
{
    int i = 1;
    if (i == 1) throw 1, 3;
    return i;
}
 
C++语言本身或者标准库抛出的异常都是 exception 的子类,称为标准异常(Standard Exception)。你可以通过下面的语句来捕获所有的标准异常:
try{
    //可能抛出异常的语句
}catch(exception &e){
    //处理异常的语句
}
 
为何C++不提供“finally”结构
因为C++提供了另一种 机制,完全可以取代finally,而且这种机制几乎总要比finally工作得更好:就是——“分配资源即初始化”。
在C++中通常使用RAII,即Resource Aquisition Is Initialization.
就是将资源封装成一个类,将资源的初始化封装在构造函数里,释放封装在析构函数里。要在局部使用资源的时候,就实例化一个local object。
在抛出异常的时候,由于local object脱离了作用域,自动调用析构函数,会保证资源被释放

C++异常处理try、catch 没有finally的更多相关文章

  1. Java 异常处理 try catch finally throws throw 的使用和解读(一)

    //最近的一个内部表决系统开发过程中,//发现对异常处理还存在一些模棱两可的地方,//所以想着整理一下//主要涉及到://1.try catch finally throws throw 的使用和解读 ...

  2. java异常处理try catch finally

    1       异常 1.1      异常处理的作用 在编程时,如果出现文件打开失败,读写文件就会异常退出.如果出现内存溢出错误,程序也会异常退出.如果不能对这些异常进行处理.程序则无法正常运行.所 ...

  3. 异常处理 try...catch...finally 执行顺序, 以及对返回值得影响

    异常处理 try...catch...finally 执行顺序, 以及对返回值得影响 结论:1.不管有没有出现异常,finally块中代码都会执行:2.当try和catch中有return时,fina ...

  4. C++异常处理: try,catch,throw,finally的用法

    写在前面 所谓异常处理,即让一个程序运行时遇到自己无法处理的错误时抛出一个异常,希望调用者可以发现处理问题. 异常处理的基本思想是简化程序的错误代码,为程序键壮性提供一个标准检测机制. 也许我们已经使 ...

  5. C#-异常处理:tyr,catch,finally ---ShinePans

    异常处理能够解决诸如一下问题: 数据库连接失败,IO错误,数据溢出,数组下表越界等问题.  总结:我认为在某些easy出错的地方加上 异常处理语句是很明智的选择 finally 是不管怎样都要运行的语 ...

  6. js中的异常处理try...catch使用介绍

    在JavaScript可以使用try...catch来进行异常处理. 例如: try { foo.bar();} catch (e) { alert(e.name + ": " + ...

  7. javascript 之异常处理try catch finally--05

    语法结构 try catch finally是ECMAScript-262 第三版提供异常处理机制的标准,语法结构如下: try{ //可能会发生的错误代码 } catch(error){ //错误处 ...

  8. 异常处理-try catch

    一:try catch是什么 try catch是java程序设计中处理异常的重要组成部分 异常是程序中的一些错误,有些异常需要做处理,有些则不需要捕获处理,异常是针对方法来说的,抛出.声明抛出.捕获 ...

  9. js的异常处理 try catch

    <script language="JavaScript"> try { throw new Error(10,"asdasdasd") } cat ...

  10. Java中的异常处理try catch(第八周课堂示例总结)

    异常处理 使用Java异常处理机制: 把可能会发生错误的代码放进try语句块中. 当程序检测到出现了一个错误时会抛出一个异常对象. 异常处理代码会捕获并处理这个错误. catch语句块中的代码用于处理 ...

随机推荐

  1. 测开之路三十一:Flask基础之请求与相应

    from flask import requestrequest.pathrequest.methodrequest.formrequest.argsrequest.values 一般用form获取p ...

  2. 重新认识Maven

    PS:第一次接触maven大约是两年前吧,隐约记得之前都是人工寻找并下载很多jar,放在项目的lib中(表示太年轻,没有接触过Ant或者其他类似的工具,就不找别人写的比较了).懒人永远有着自己的小聪明 ...

  3. PHP将mysql数据表转换为excel文件

    测试代码: <?php $DB_Server = "127.0.0.1"; $DB_Username = "root"; $DB_Password = & ...

  4. Vim使用taglist功能

    下载 wget http://www.vim.org/scripts/download_script.php?src_id=19574 -O taglist.zip 然后解压taglist.zip到~ ...

  5. PAT_A1074#Reversing Linked List

    Source: PAT A1074 Reversing Linked List (25 分) Description: Given a constant K and a singly linked l ...

  6. Selenium:多表单(frame/iframe)切换(Switch模块)

    frame标签有frameset.frame.iframe三种,frameset跟其他普通标签没有区别,不会影响到正常的定位,而frame与iframe需要切换进去才能定位到其中的元素 比如下面这个网 ...

  7. python使用消息队列RabbitMq(进阶)

    import pika connection = pika.BlockingConnection(pika.ConnectionParameters( 'localhost')) channel = ...

  8. (4.10)sql server导入导出

    SQL Server快速导入数据分享 核心参考:官网~~https://docs.microsoft.com/zh-cn/sql/relational-databases/import-export/ ...

  9. SpringBoot入门简介

    SpringBoot诞生的背景 所有软件行业里面,如果要说商用体系,排在第一位的永远是java,因为java的体系丰富,支持度高,安全性也高 但是我们所有的开发者也不得不去忍受Java中的以下痛苦 举 ...

  10. IDEA compile successfully many errors still occur

    Compile and install successfully with maven in IDEA, but error prompt still popup. Your local enviro ...