0.目录

1.C语言异常处理

2.C++中的异常处理

3.小结

1.C语言异常处理

异常的概念:

  • 程序在运行过程中可能产生异常
  • 异常(Exception)与 Bug 的区别
    1. 异常是程序运行时可预料的执行分支
    2. Bug 是程序的错误,是不被预期的运行方式

异常(Exception)与 Bug 的对比:

  • 异常

    1. 运行时产生除0的情况
    2. 需要打开的外部文件不存在
    3. 数组访问时越界
  • Bug
    1. 使用野指针
    2. 堆数组使用结束后未释放
    3. 选择排序无法处理长度为0的数组

异常处理的方式:

C语言经典处理方式:if ... else ...

示例——除法操作异常处理:

#include <iostream>

using namespace std;

double divide(double a, double b, int* valid)
{
const double delta = 0.000000000000001;
double ret = 0; if( !((-delta < b) && (b < delta)) )
{
ret = a / b; *valid = 1;
}
else
{
*valid = 0;
} return ret;
} int main(int argc, char *argv[])
{
int valid = 0;
double r = divide(1, 0, &valid); if( valid )
{
cout << "r = " << r << endl;
}
else
{
cout << "Divided by zero..." << endl;
} return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Divided by zero...

缺陷:

  • divide函数有3个参数,难以理解其用法
  • divide函数调用后必须判断valid代表的结果
    1. 当valid为true时,运算结果正常
    2. 当valid为false时,运算结果出现异常

通过 setjmp() 和 longjmp() 进行优化:

  • int setjmp(jmp_buf env)

    1. 将当前上下文保存在jmp_buf结构体中
  • void longjmp(jmp_buf env, int val)
    1. 从jmp_buf结构体中恢复setjmp()保存的上下文
    2. 最终从setjmp函数调用点返回,返回值为val

示例——除法操作异常处理优化:

#include <iostream>
#include <csetjmp> using namespace std; static jmp_buf env; double divide(double a, double b)
{
const double delta = 0.000000000000001;
double ret = 0; if( !((-delta < b) && (b < delta)) )
{
ret = a / b;
}
else
{
longjmp(env, 1);
} return ret;
} int main(int argc, char *argv[])
{
if( setjmp(env) == 0 )
{
double r = divide(1, 0); cout << "r = " << r << endl;
}
else
{
cout << "Divided by zero..." << endl;
} return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Divided by zero...

setjmp() 和 longjmp() 的引入:

  • 必然涉及到使用全局变量
  • 暴力跳转导致代码可读性降低
  • 本质还是if ... else ... 异常处理方式

C语言中的经典异常处理方式会使得程序中逻辑中混入大量的处理异常的代码。

正常逻辑代码和异常处理代码混合在一起,导致代码迅速膨胀,难以维护。。。

示例——异常处理代码分析:

#include <iostream>

using namespace std;

#define SUCCESS           0
#define INVALID_POINTER -1
#define INVALID_LENGTH -2
#define INVALID_PARAMETER -3 int MemSet(void* dest, unsigned int length, unsigned char v)
{
if( dest == NULL )
{
return INVALID_POINTER;
} if( length < 4 )
{
return INVALID_LENGTH;
} if( (v < 0) || (v > 9) )
{
return INVALID_PARAMETER;
} unsigned char* p = (unsigned char*)dest; for(int i=0; i<length; i++)
{
p[i] = v;
} return SUCCESS;
} int main(int argc, char *argv[])
{
int ai[5];
int ret = MemSet(ai, sizeof(ai), 0); if( ret == SUCCESS )
{
}
else if( ret == INVALID_POINTER )
{
}
else if( ret == INVALID_LENGTH )
{
}
else if( ret == INVALID_PARAMETER )
{
} return ret;
}

2.C++中的异常处理

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

  • try语句处理正常代码逻辑
  • catch语句处理异常情况
  • try语句中的异常由对应的catch语句处理

C++通过throw语句抛出异常信息:

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

  • 当前函数能够处理异常,程序继续往下执行
  • 当前函数无法处理异常,则函数停止执行,并返回

未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,否则程序将停止执行。

示例——C++异常处理:

#include <iostream>

using namespace std;

double divide(double a, double b)
{
const double delta = 0.000000000000001;
double ret = 0; if( !((-delta < b) && (b < delta)) )
{
ret = a / b;
}
else
{
throw 0;
} return ret;
} int main(int argc, char *argv[])
{
try
{
double r = divide(1, 0); cout << "r = " << r << endl;
}
catch(...)
{
cout << "Divided by zero..." << endl;
} return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Divided by zero...

同一个try语句可以跟上多个catch语句:

  • catch语句可以定义具体处理的异常类型
  • 不同类型的异常由不同的catch语句负责处理
  • try语句中可以抛出任何类型的异常
  • catch(...)用于处理所有类型的异常
  • 任何异常都只能被捕获(catch)一次

异常处理的匹配规则:

示例——异常类型匹配:

#include <iostream>

using namespace std;

void Demo1()
{
try
{
throw 'c';
}
catch(char c)
{
cout << "catch(char c)" << endl;
}
catch(short c)
{
cout << "catch(short c)" << endl;
}
catch(double c)
{
cout << "catch(double c)" << endl;
}
catch(...)
{
cout << "catch(...)" << endl;
}
} void Demo2()
{
throw string("HelloWorld!");
} int main(int argc, char *argv[])
{
Demo1(); try
{
Demo2();
}
catch(char* s)
{
cout << "catch(char *s)" << endl;
}
catch(const char* cs)
{
cout << "catch(const char *cs)" << endl;
}
catch(string ss)
{
cout << "catch(string ss)" << endl;
} return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
catch(char c)
catch(string ss)

catch语句块中可以抛出异常:

为什么要在catch中重新抛出异常?

  • catch中捕获的异常可以被重新解释后抛出
  • 工程开发中使用这样的方式统一异常类型

示例——在catch中重新抛出异常:

#include <iostream>

using namespace std;

void Demo()
{
try
{
try
{
throw 'c';
}
catch(int i)
{
cout << "Inner: catch(int i)" << endl;
throw i;
}
catch(...)
{
cout << "Inner: catch(...)" << endl;
throw;
}
}
catch(...)
{
cout << "Outer: catch(...)" << endl;
}
} int main(int argc, char *argv[])
{
Demo(); return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Inner: catch(...)
Outer: catch(...)

示例——异常的重新解释:

#include <iostream>

using namespace std;

/*
假设: 当前的函数式第三方库中的函数,因此,我们无法修改源代码 函数名: 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 "Invalid Parameter";
break;
case -2:
throw "Runtime Exception";
break;
case -3:
throw "Timeout Exception";
break;
}
}
} int main(int argc, char *argv[])
{
try
{
MyFunc(11);
}
catch(const char* cs)
{
cout << "Exception Info: " << cs << endl;
} return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Exception Info: Timeout Exception

C++中的异常处理:

  • 异常的类型可以是自定义类类型
  • 对于类类型异常的匹配依旧是至上而下严格匹配
  • 赋值兼容性原则在异常匹配中依然适用
  • 一般而言
    1. 匹配子类异常的catch放在上部
    2. 匹配父类异常的catch放在下部

(根据赋值兼容性原则:子类的异常对象可以被父类的catch语句块抓住。)

C++中的异常处理:

  • 在工程中会定义一系列的异常类
  • 每个类代表工程中可能出现的一种异常类型
  • 代码复用时可能需要重解释不同的异常类
  • 在定义catch语句块时推荐使用引用作为参数

示例——类类型的异常:

#include <iostream>

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)
{
cout << "Exception Info: " << endl;
cout << " ID: " << e.id() << endl;
cout << " Description: " << e.description() << endl;
}
catch(const Base& e)
{
cout << "catch(const Base& e)" << endl;
} return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Exception Info:
ID: -3
Description: Timeout Exception

C++中的异常处理:

  • C++标准库中提供了实用异常类族
  • 标准库中的异常都是从exception类派生的
  • exception类有两个主要的分支
    1. logic_error——常用于程序中的可避免逻辑错误
    2. runtime_error——常用于程序中无法避免的恶性错误

标准库中的异常:

示例——标准库中的异常使用(优化之前的Array.h和HeapArray.h):

// Array.h
#ifndef _ARRAY_H_
#define _ARRAY_H_ #include <stdexcept> using namespace std; template
< typename T, int N >
class Array
{
T m_array[N];
public:
int length() const;
bool set(int index, T value);
bool get(int index, T& value);
T& operator[] (int index);
T operator[] (int index) const;
virtual ~Array();
}; template
< typename T, int N >
int Array<T, N>::length() const
{
return N;
} template
< typename T, int N >
bool Array<T, N>::set(int index, T value)
{
bool ret = (0 <= index) && (index < N); if( ret )
{
m_array[index] = value;
} return ret;
} template
< typename T, int N >
bool Array<T, N>::get(int index, T& value)
{
bool ret = (0 <= index) && (index < N); if( ret )
{
value = m_array[index];
} return ret;
} template
< typename T, int N >
T& Array<T, N>::operator[] (int index)
{
if( (0 <= index) && (index < N) )
{
return m_array[index];
}
else
{
throw out_of_range("T& Array<T, N>::operator[] (int index)");
}
} template
< typename T, int N >
T Array<T, N>::operator[] (int index) const
{
if( (0 <= index) && (index < N) )
{
return m_array[index];
}
else
{
throw out_of_range("T Array<T, N>::operator[] (int index) const");
}
} template
< typename T, int N >
Array<T, N>::~Array()
{
} #endif
// HeapArray.h
#ifndef _HEAPARRAY_H_
#define _HEAPARRAY_H_ #include <stdexcept> using namespace std; template
< typename T >
class HeapArray
{
private:
int m_length;
T* m_pointer; HeapArray(int len);
HeapArray(const HeapArray<T>& obj);
bool construct();
public:
static HeapArray<T>* NewInstance(int length);
int length() const;
bool get(int index, T& value);
bool set(int index ,T value);
T& operator [] (int index);
T operator [] (int index) const;
HeapArray<T>& self();
const HeapArray<T>& self() const;
~HeapArray();
}; template
< typename T >
HeapArray<T>::HeapArray(int len)
{
m_length = len;
} template
< typename T >
bool HeapArray<T>::construct()
{
m_pointer = new T[m_length]; return m_pointer != NULL;
} template
< typename T >
HeapArray<T>* HeapArray<T>::NewInstance(int length)
{
HeapArray<T>* ret = new HeapArray<T>(length); if( !(ret && ret->construct()) )
{
delete ret;
ret = 0;
} return ret;
} template
< typename T >
int HeapArray<T>::length() const
{
return m_length;
} template
< typename T >
bool HeapArray<T>::get(int index, T& value)
{
bool ret = (0 <= index) && (index < length()); if( ret )
{
value = m_pointer[index];
} return ret;
} template
< typename T >
bool HeapArray<T>::set(int index, T value)
{
bool ret = (0 <= index) && (index < length()); if( ret )
{
m_pointer[index] = value;
} return ret;
} template
< typename T >
T& HeapArray<T>::operator [] (int index)
{
if( (0 <= index) && (index < length()) )
{
return m_pointer[index];
}
else
{
throw out_of_range("T& HeapArray<T>::operator [] (int index)");
}
} template
< typename T >
T HeapArray<T>::operator [] (int index) const
{
if( (0 <= index) && (index < length()) )
{
return m_pointer[index];
}
else
{
throw out_of_range("T HeapArray<T>::operator [] (int index) const");
}
} template
< typename T >
HeapArray<T>& HeapArray<T>::self()
{
return *this;
} template
< typename T >
const HeapArray<T>& HeapArray<T>::self() const
{
return *this;
} template
< typename T >
HeapArray<T>::~HeapArray()
{
delete[]m_pointer;
} #endif
// test.cpp
#include <iostream>
#include "Array.h"
#include "HeapArray.h" using namespace std; void TestArray()
{
Array<int, 5> a; for(int i=0; i<a.length(); i++)
{
a[i] = i;
} for(int i=0; i<a.length(); i++)
{
cout << a[i] << endl;
}
} void TestHeapArray()
{
HeapArray<double>* pa = HeapArray<double>::NewInstance(5); if( pa != NULL )
{
HeapArray<double>& array = pa->self(); for(int i=0; i<array.length(); i++)
{
array[i] = i;
} for(int i=0; i<array.length(); i++)
{
cout << array[i] << endl;
}
} delete pa;
} int main(int argc, char *argv[])
{
try
{
TestArray(); cout << endl; TestHeapArray();
}
catch(...)
{
cout << "Exception" << endl;
} return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
0
1
2
3
4 0
1
2
3
4

3.小结

  • 程序中不可避免的会发生异常
  • 异常是在开发阶段就可以预见的运行时问题
  • C语言中通过经典的if .... else ... 方式处理异常
  • C++中存在更好的异常处理方式
  • C++中直接支持异常处理的概念
  • try ... catch ... 是C++中异常处理的专用语句
  • try语句处理正常代码逻辑,catch语句处理异常情况
  • 同一个try语句可以跟上多个catch语句
  • 异常处理必须严格匹配,不进行任何的类型转换
  • catch语句块中可以抛出异常
  • 异常的类型可以是自定义类类型
  • 赋值兼容性原则在异常匹配中依然适用
  • 标准库中的异常都是从exception类派生的

C++解析(28):异常处理的更多相关文章

  1. drf框架的解析模块-异常处理模块-响应模块-序列化模块

    解析模块 为什么要配置解析模块 (1).drf给我们通过了多种解析数据包方式的解析类. (2).我们可以通过配置来控制前台提交的那些格式的数据台解析,那些数据不解析. (3).全局配置就是针对一个视图 ...

  2. SpringMVC源码解析 - HandlerMethod

    HandlerMethod及子类主要用于封装方法调用相关信息,子类还提供调用,参数准备和返回值处理的职责. 分析下各个类的职责吧(顺便做分析目录): HandlerMethod 封装方法定义相关的信息 ...

  3. springboot优雅实现异常处理

    前言 在平时的 API 开发过程中,总会遇到一些错误异常没有捕捉到的情况.那有的小伙伴可能会想,这还不简单么,我在 API 最外层加一个 try...catch 不就完事了. 哈哈哈,没错.这种方法简 ...

  4. 2. C/C++笔试面试经典题目二

    1. C和C++中struct有什么区别? [参考答案] [解析]C中的struct没有保护行为,没有public,private,protected,内部不能有函数,但可以有函数指针. 2. C++ ...

  5. java中堆栈(stack)和堆(heap)(还在问静态变量放哪里,局部变量放哪里,静态区在哪里.....进来)

    (1)内存分配的策略 按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编 译时就可以给 ...

  6. 四、Spring——Spring MVC

    Spring MVC 1.Spring MVC概述 Spring MVC框架围绕DispatcherServlet这个核心展开,DispatcherServlet负责截获请求并将其分配给响应的处理器处 ...

  7. Java内存分配和内存管理

    首先是概念层面的几个问题: Java中运行时内存结构有哪几种? Java中为什么要设计堆栈分离? Java多线程中是如何实现数据共享的? Java反射的基础是什么? 然后是运用层面: 引用类型变量和对 ...

  8. The P4 Language Specification v1.0.2 Parser

    <p4规范>解析器部分详解 p4解析器是根据有限状态机的思想来设计的. 解析器中解析的过程可以被一个解析图(parser graph)所表示,解析图中所表示的某一个状态(或者说,在P4语言 ...

  9. servlet&jsp高级:第二部分

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

随机推荐

  1. 关于快速沃尔什变换(FWT)的一些个人理解

    定义 FWT是一种快速完成集合卷积运算的算法. 它可以用于求解类似 $C[i]=\sum\limits_{j⊗k=i}A[j]*B[k]$ 的问题. 其中⊗代表位运算中的|,&,^的其中一种. ...

  2. Azkaban系统的安装和分析。

    Azkaban系统是一个数据处理的很好用的工具,可以用来运行hadoop任务,管理hdfs,可以进行schedule任务调度,总体来说功能还是很强大的. 研究了一下azkaban,做了以下总结性的东西 ...

  3. JS 判断checkbox 是否选中

    <input type="checkbox" id="IsEnable" /> 在调试的时候,会出现,一直未true的状态,不管是选中还是未选中 解 ...

  4. C++自学第二课:对象和类的概念

    既然是C++,比C语言多了最重要的概念:面向对象. 面向对象?对象是什么?Girlfriend? 我天天面向她也没学会C++. 我觉得对象就是有统一特征的一类编程目标. 打个比方说墙上有个开关,我一按 ...

  5. 学习笔记之glog的使用

    下载源码,使用cmake编译,最后得到了32位的静态库 glog.lib 使用库时要注意添加以下预定义: GLOG_NO_ABBREVIATED_SEVERITIES; GOOGLE_GLOG_DLL ...

  6. vue的ui库使用Element UI,纯html页面,不使用webpack那玩意

    使用手册访问:https://cloud.tencent.com/developer/doc/1270 第一步:在head添加样式 <link rel="stylesheet" ...

  7. Zookeeper-----Zookeeper概述

    一:Zookeeper的概念? ZooKeeper是一种分布式协调服务,用于管理大型主机.在分布式环境中协调和管理服务是一个复杂的过程.ZooKeeper通过其简单的架构和API解决了这个问题.Zoo ...

  8. 【python 3.6】如何将list存入txt后,再读出list

    今天遇到一个需求,就是将一个list文件读取后,存入一个txt配置文件.存入时,发现list文件无法直接存入,必须转为str模式. 但在读取txt时,就无法恢复成list类型来读取了(准确地说,即使强 ...

  9. centos 6.5 双网卡 上网 virtualbox nat hostonly

    虚拟机两张网卡:分别调成NAT(eth0)和host only(eht1)模式. nat的网卡不用设置,host only网卡调为(vi /etc/sysconfig/network-scripts/ ...

  10. asp.net 设计条码code 11的问题

    前一段时间思考了一些条码生成的问题,其实条码也可以说是加密的文件显示. 一个条码首先要有规定 比如code 11 又 1234567890 - 这11个字符组成 而1 又用 5码 表示 "1 ...