专栏首页Linux驱动29.C++- 异常处理

29.C++- 异常处理

C++内置了异常处理的语法元素 try catch

try语句处理正常代码逻辑

  • try语句发现异常时,则通过throw语句抛出异常,并退出try语句

catch语句处理异常情况

  • throw语句抛出异常时,则会直接跳到catch语句处理
  • catch语句允许被重载,在try语句后面可以有多个catch语句
  • 不同类型的异常由不同的catch语句捕获,顺序从上往下严格匹配,不会进行隐式转换,比如:
throw 1;       //由int型的catch语句捕获
throw 1.5;     //由double型的catch语句捕获
throw 1.5f;    //由float型的catch语句捕获
throw 'A';      //由char型的catch语句捕获
throw "ABC";    //由char const *型的catch语句捕获
throw string("ABC");      //由string型的catch语句捕获
  • cath(...)语句,表示捕获前面所有没被定义的异常,且只能放在所有catch语句的末尾,比如:
    try
    {
              throw 1.55;          //直接退出try语句,跳转到满足条件的catch语句
              throw "ERR";   
    }
    catch(int i)                //只捕获int型异常
    {
              cout<<i<<endl;
    }
    catch(string s)           //只捕获string型异常
    {
        cout<<s<<endl;
    }
    catch(...)                //捕获前面所有没被定义的异常
    {
        cout<<"cath(...)"<<endl;
    }

运行打印:

cath(...)

throw抛出的异常必须被catch处理

如果throw抛出的异常,在当前函数没有catch语句能捕获,则会返回到上一级函数里再寻找catch语句,直到被处理为止,否则程序将结束运行,如下图:

在catch语句块中可以将捕获的异常重新抛出

catch抛出的异常,则需要通过外层的try...catch...捕获

如果是catch(...)语句,则直接填throw;即可,编译器会自动将捕获的异常重新抛出

比如:

void internal()
{
             try
            {
                     throw 1;
            }
             catch(...)
            {
                     cout<< "internal: catch(...)"<<endl;
                     throw;
            }
}

int main()
{

   try
   {
             internal();      
   }

   catch(int i)
   {
          switch(i)
          {
             case 1 :                                            //1对应超时
                     cout<<"timeout"<<endl;  break;
              case 2 :                                           //2对应实参有误
                     cout<<"invalid argument"<<endl; break;
              case 3 :                                           //3对应运行异常
                     cout<<"runtime exception"<<endl; break;
          } 
   }
   return 0;

}

运行打印:

internal: catch(...)
timeout

catch中重新抛出异常的意义

举个例子,当我们调用第三方库的func()函数,但是该func()函数返回的异常是int型,每个异常值意义大有不同 (每次查看异常值都需要翻看文档手册才行)

所以我们可以在自己库创建一个myfunc()函数,通过try...catch...再次封装func()函数,将异常值重新解释为其它类型(比如const char *),然后再次抛出.

以后调用myfunc()函数,获取的异常信息就是const char *类型了.

如果catch中抛出的类型是类的情况

  • 需要将捕获子类异常的catch放在上部
  • 捕获父类异常的cath放在下部, 避免子类异常当做父类异常来使用.

比如:

#include <iostream>
#include <string>

using namespace std;

class Base
{
};

class Exception : public Base
{
    int m_id;                        //异常值
    string m_desc;               //异常值描述信息
public:
    Exception(int id, string desc)
    {
        m_id = id;
        m_desc = desc;
    }

    int id() const
    {
        return m_id;
    }

    string description() const
    {
        return m_desc;
    }
};


/*
    假设: 当前的函数式第三方库中的函数,因此,我们无法修改源代码
    函数名: void func(int i)
    抛出异常的类型: int
                        -1 ==》 参数异常
                        -2 ==》 运行异常
                        -3 ==》 超时异常
*/
void func(int i)
{
    if( i < 0 )
    {
        throw -1;
    }
    if( i > 100 )
    {
        throw -2;
    }
    if( i == 11 )
    {
        throw -3;
    }

    cout << "Run func..." << endl;
}

void MyFunc(int i)
{
    try
    {
        func(i);
    }

    catch(int i)
    {
        switch(i)
        {
            case -1:
                throw Exception(-1, "Invalid Parameter");              //生成一个子类对象,并抛出
                break;

            case -2:
                throw Exception(-2, "Runtime Exception");
                break;

            case -3:
                throw Exception(-3, "Timeout Exception");
                break;
        }
    }
}

int main(int argc, char *argv[])
{
    try
    {
        MyFunc(11);
    }      

    catch(const Exception& e)              //捕获子类异常的catch放在上部
    {
        cout << "Exception Info: " << endl;
        cout << "   ID: " << e.id() << endl;
        cout << "   Description: " << e.description() << endl;
    }

    catch(const Base& e)                 //捕获父类异常的catch放在下部
    {
        cout << "catch(const Base& e)" << endl;
    }
    return 0;
}

运行打印:

Exception Info:
   ID: -3
   Description: Timeout Exception

在C++标准库中提供了异常类

头文件 : <stdexcept>

标准库中的异常都是从exception类派生的

exception类主要有两个分支

- logic_error

用于程序中可避免的逻辑错误,在程序运行之前,就能被检测到

logic_error类派生了以下几种类:

  • domain_error(const string& )    :   专业领域内的范畴
  • invalid_argument(const string& )   :  无效参数,比如对unsigned型进行负数操作
  • length_error(const string& )  :    长度异常,比如字符串附加太多字符
  • out_of_range(const string&)     :    超出范围,比如数组下标越界
  • 它们都有一个what()成员函数,用来返回一个字符串异常信息

-runtime_error

常用于程序中无法避免的恶性错误,只在程序运行时才能被检测到

logic_error类派生了以下几种类:

  • range_error(const string& )  :内部计算时发生区间错误
  • overflow_error(const string& )  :算数运算时发生上溢
  • underflow_error(const string& )  :算数运算时发生下溢
  • 它们都有一个what()成员函数,用来返回一个字符串异常信息

比如:

#include <iostream> 
#include <stdexcept>

using namespace std;

template <typename T, int N >
class Array
{
       T ma[N];public:
       Array()
       { 
              for(int i=0;i<N;i++)
              ma[N]=0;
       }

       T& operator [] (int index)
       {  
              if((index>=0)&&(index<N))
              {
                     return ma[index];
              }

              else                      //数组下标越界
              {
                  throw out_of_range("T& operator [] (int index)"); //抛出一个 out_of_range类
              }  
       }
};

int main()
{
       try
       {
              Array<int,5> arr;
              arr[10]=100;
       }
       catch(out_of_range& exc)
       {
              cout<< exc.what()<<endl;                                           //打印捕获到out_of_range类的异常信息
              cout<< " Line: " << __LINE__ <<", Function: "<< __FUNCTION__ << endl;  //打印当前行
       }
       return 0;
}

运行打印:

T& operator [] (int index)
Line: 41, Function: main

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 25.C++- 泛型编程之函数模板(详解)

    本章学习: 1)初探函数模板 2)深入理解函数模板 3)多参函数模板 4)重载函数和函数模板 ---- 当我们想写个Swap()交换函数时,通常这样写: voi...

    张诺谦
  • 贪心算法-跳跃游戏二

    给定一个非负整数数组,假定你的初始位置为数组第一个下标。 数组中的每个元素代表你在那个位置能够跳跃的最大长度。 你的目标是到达最后一个下标,并且使用最少的跳跃次...

    张诺谦
  • 2.C++中的bool类型,三目运算符,引用

    在C++中,bool类型只有true(非0)和flase(0)两个值,且bool类型只占用了一个字节.

    张诺谦
  • 干货,不小心执行了rm -f,除了跑路,如何恢复?

    每当我们在生产环境服务器上执行rm命令时,总是提心吊胆的,因为一不小心执行了误删,然后就要准备跑路了,毕竟人不是机器,更何况机器也有bug,呵呵。

    芋道源码
  • 基于repmgr的postgresql主备高可用方案

    本文比较基础,主要介绍postgresql开源高可用工具repmgr的部署和使用,初学者可以根据本文步骤一步一步做下去,废话不多说,直接进入主题,本文以两台机器...

    数据库架构之美
  • python读写csv文件的实战

    csv是什么?大家估计都听过,不过我猜很少能有人比较全面的解释下的,那么小弟就献丑一下。csv我理解的是一个存储数据的文件,里面以逗号作为分割进行存储(当然也可...

    py3study
  • 2D-Driven 3D Object Detection in RGB-D Images

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    于小勇
  • C++版 - LeetCode 144. Binary Tree Preorder Traversal (二叉树先根序遍历,非递归)

    提交网址: https://leetcode.com/problems/binary-tree-preorder-traversal/

    Enjoy233
  • 链队 原

    队列用链表来表示时,需要用两个变量来记录队列两端的变化:theFront,theBack.

    青木
  • Spring Security OAuth 个性化token

    Pig 基于Spring Cloud、oAuth2.0开发基于Vue前后分离的开发平台,支持账号、短信、SSO等多种登录,提供配套视频开发教程。 https:...

    冷冷

扫码关注云+社区

领取腾讯云代金券