C++中异常处理的基本思想及throw语句抛出异常的使用

 更新时间:2020年4月25日 17:36  点击:1569

异常处理基本思想
C++的异常处理的基本思想大致可以概括为传统错误处理机制、通过函数返回值来处理错误。

2016314153351173.jpg (377×335)

1)C++的异常处理机制使得异常的引发和异常的处理不必在同一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以再适当的位置设计对不同类型异常的处理。
2)异常是专门针对抽象编程中的一系列错误处理的,C++中不能借助函数机制,因为栈结构的本质是先进后出,依次访问,无法进行跳跃,但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试,如图

3)异常超脱于函数机制,决定了其对函数的跨越式回跳。
4)异常跨越函数

异常基本语法

2016314153429533.jpg (577×329)

1) 若有异常则通过throw操作创建一个异常对象并抛掷。
2) 将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句,然后执行try块内的保护段。
3) 如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行。程序从try块后跟随的最后一个catch子句后面的语句继续执行下去。
4) catch子句按其在try块后出现的顺序被检查。匹配的catch子句将捕获并处理异常(或继续抛掷异常)。
5) 如果匹配的处理器未找到,则运行函数terminate将被自动调用,其缺省功能是调用abort终止程序。
6)处理不了的异常,可以在catch的最后一个分支,使用throw语法,向上扔
7)异常机制与函数机制互不干涉,但捕捉的方式是基于类型匹配。捕捉相当于函数返回类型的匹配,而不是函数参数的匹配,所以捕捉不用考虑一个抛掷中的多种数据类型匹配问题。
catch代码块必须出现在try后,并且在try块后可以出现多个catch代码块,以捕捉各种不同类型的抛掷。
异常机制是基于这样的原理:程序运行实质上是数据实体在做一些操作,因此发生异常现象的地方,一定是某个实体出了差错,该实体所对应的数据类型便作为抛掷和捕捉的依据。
8)异常捕捉严格按照类型匹配
 异常捕捉的类型匹配之苛刻程度可以和模板的类型匹配媲美,它不允许相容类型的隐式转换,比如,抛掷char类型用int型就捕捉不到.例如下列代码不会输出“int exception.”,从而也不会输出“That's ok.” 因为出现异常后提示退出

int main(){ 
  try{ 
    throw ‘H'; 
  } 
  catch (int){ 
    cout << "int exception.\n"; 
  } 
  cout << "That's ok.\n"; 
 
  return 0; 
} 

栈解旋(unwinding)
异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋(unwinding)。

#include <iostream> 
#include <cstdio> 
using namespace std; 
 
 
class MyException {}; 
 
class Test 
{ 
public: 
  Test(int a = 0, int b = 0) 
  { 
    this->a = a; 
    this->b = b; 
    cout << "Test 构造函数执行" << "a:" << a << " b: " << b << endl; 
  } 
  void printT() 
  { 
    cout << "a:" << a << " b: " << b << endl; 
  } 
  ~Test() 
  { 
    cout << "Test 析构函数执行" << "a:" << a << " b: " << b << endl; 
  } 
private: 
  int a; 
  int b; 
}; 
 
void myFunc() throw (MyException) 
{ 
  Test t1; 
  Test t2; 
 
  cout << "定义了两个栈变量,异常抛出后测试栈变量的如何被析构" << endl; 
 
  throw MyException(); 
} 
 
int main() 
{ 
  //异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象, 
  //都会被自动析构。析构的顺序与构造的顺序相反。 
  //这一过程称为栈的解旋(unwinding) 
  try 
  { 
    myFunc(); 
  } 
  //catch(MyException &e) //这里不能访问异常对象 
  catch (MyException) //这里不能访问异常对象 
  { 
    cout << "接收到MyException类型异常" << endl; 
  } 
  catch (...) 
  { 
    cout << "未知类型异常" << endl; 
  } 
 
  return 0; 
} 

异常接口声明
1)为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型,例如:
void func() throw (A, B, C , D); //这个函数func()能够且只能抛出类型A B C D及其子类型的异常。
2)如果在函数声明中没有包含异常接口声明,则次函数可以抛掷任何类型的异常,例如:
void func();
3)一个不抛掷任何类型异常的函数可以声明为:
void func() throw();
4) 如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexpected函数会被调用,该函数默认行为调用terminate函数中止程序。

传统处理错误

#include <iostream> 
#include <cstdio> 
using namespace std; 
 
// 传统的错误处理机制 
int myStrcpy(char *to, char *from) 
{ 
  if (from == NULL) { 
    return 1; 
  } 
  if (to == NULL) { 
    return 2; 
  } 
 
  // copy时的场景检查 
  if (*from == 'a') { 
    return 3; // copy时错误 
  } 
  while (*from != '\0') { 
    *to = *from; 
    to++; 
    from++; 
  } 
  *to = '\0'; 
 
  return 0; 
} 
 
int main() 
{ 
  int ret = 0; 
  char buf1[] = "zbcdefg"; 
  char buf2[1024] = { 0 }; 
 
  ret = myStrcpy(buf2, buf1); 
  if (ret != 0) { 
    switch (ret) { 
    case 1: 
      cout << "源buf出错!\n"; 
      break; 
    case 2: 
      cout << "目的buf出错!\n"; 
      break; 
    case 3: 
      cout << "copy过程出错!\n"; 
      break; 
    default: 
      cout << "未知错误!\n"; 
      break; 
    } 
  } 
  cout << "buf2:\n" << buf2; 
  cout << endl; 
 
  return 0; 
} 

throw char*

#include <iostream> 
#include <cstdio> 
using namespace std; 
 
// throw char * 
void myStrcpy(char *to, char *from) 
{ 
  if (from == NULL) { 
    throw "源buf出错"; 
  } 
  if (to == NULL) { 
    throw "目的buf出错"; 
  } 
 
  // copy时的场景检查 
  if (*from == 'a') { 
    throw "copy过程出错"; // copy时错误 
  } 
  while (*from != '\0') { 
    *to = *from; 
    to++; 
    from++; 
  } 
  *to = '\0'; 
 
  return; 
} 
 
int main() 
{ 
  int ret = 0; 
  char buf1[] = "abcdefg"; 
  char buf2[1024] = { 0 }; 
 
  try 
  { 
    myStrcpy(buf2, buf1); 
  } 
  catch (int e) // e可以写可以不写 
  { 
    cout << e << "int类型异常" << endl; 
  } 
  catch (char *e) 
  { 
    cout << "char* 类型异常" << endl; 
  } 
  catch (...) 
  { 
  }; 
  cout << endl; 
 
  return 0; 
} 

throw 类对象

#include <iostream> 
#include <cstdio> 
using namespace std; 
 
class BadSrcType {}; 
class BadDestType {}; 
class BadProcessType 
{ 
public: 
  BadProcessType() 
  { 
    cout << "BadProcessType构造函数do \n"; 
  } 
 
 
  BadProcessType(const BadProcessType &obj) 
  { 
    cout << "BadProcessType copy构造函数do \n"; 
  } 
 
  ~BadProcessType() 
  { 
    cout << "BadProcessType析构函数do \n"; 
  } 
 
}; 

 
throw 类对象、类型异常 

void my_strcpy3(char *to, char *from) 
{ 
  if (from == NULL) 
  { 
    throw BadSrcType(); 
  } 
  if (to == NULL) 
  { 
    throw BadDestType(); 
  } 
 
  //copy是的 场景检查 
  if (*from == 'a') 
  { 
    printf("开始 BadProcessType类型异常 \n"); 
    throw BadProcessType(); //会不会产生一个匿名对象? 
  } 
 
  if (*from == 'b') 
  { 
    throw &(BadProcessType()); //会不会产生一个匿名对象? 
  } 
 
  if (*from == 'c') 
  { 
    throw new BadProcessType; //会不会产生一个匿名对象? 
  } 
  while (*from != '\0') 
  { 
    *to = *from; 
    to++; 
    from++; 
  } 
  *to = '\0'; 
} 
 
int main() 
{ 
  int ret = 0; 
  char buf1[] = "cbbcdefg"; 
  char buf2[1024] = { 0 }; 
 
  try 
  { 
    //my_strcpy1(buf2, buf1); 
    //my_strcpy2(buf2, buf1); 
    my_strcpy3(buf2, buf1); 
  } 
  catch (int e) //e可以写 也可以不写 
  { 
    cout << e << " int类型异常" << endl; 
  } 
  catch (char *e) 
  { 
    cout << e << " char* 类型异常" << endl; 
  } 
 
  //--- 
  catch (BadSrcType e) 
  { 
    cout << " BadSrcType 类型异常" << endl; 
  } 
  catch (BadDestType e) 
  { 
    cout << " BadDestType 类型异常" << endl; 
  } 
  //结论1: 如果 接受异常的时候 使用一个异常变量,则copy构造异常变量.  
  /* 
  catch( BadProcessType e) //是把匿名对象copy给e 还是e还是那个匿名对象 
  { 
  cout << " BadProcessType 类型异常" << endl; 
  } 
  */ 
  /*结论2: 使用引用的话 会使用throw时候的那个对象 
  catch( BadProcessType &e) //是把匿名对象copy给e 还是e还是那个匿名对象 
  { 
  cout << " BadProcessType 类型异常" << endl; 
  } 
  */ 
 
  //结论3: 指针可以和引用/元素写在一块 但是引用和元素不能写在一块 
  catch (BadProcessType *e) //是把匿名对象copy给e 还是e还是那个匿名对象 
  { 
    cout << " BadProcessType 类型异常" << endl; 
    delete e; 
  } 
 
  //结论4: 类对象时, 使用引用比较合适  
 
  // -- 
  catch (...) 
  { 
    cout << "未知 类型异常" << endl; 
  } 
 
  return 0; 
} 


[!--infotagslink--]

相关文章

  • C++ STL标准库std::vector的使用详解

    vector是表示可以改变大小的数组的序列容器,本文主要介绍了C++STL标准库std::vector的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2022-03-06
  • C++中取余运算的实现

    这篇文章主要介绍了C++中取余运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • 详解C++ string常用截取字符串方法

    这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • C++中四种加密算法之AES源代码

    本篇文章主要介绍了C++中四种加密算法之AES源代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...2020-04-25
  • C++ 整数拆分方法详解

    整数拆分,指把一个整数分解成若干个整数的和。本文重点给大家介绍C++ 整数拆分方法详解,非常不错,感兴趣的朋友一起学习吧...2020-04-25
  • C++中 Sort函数详细解析

    这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
  • C++万能库头文件在vs中的安装步骤(图文)

    这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • 详解C++ bitset用法

    这篇文章主要介绍了C++ bitset用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • 浅谈C++中的string 类型占几个字节

    本篇文章小编并不是为大家讲解string类型的用法,而是讲解我个人比较好奇的问题,就是string 类型占几个字节...2020-04-25
  • C#多线程中的异常处理操作示例

    这篇文章主要介绍了C#多线程中的异常处理操作,涉及C#多线程及异常的捕获、处理等相关操作技巧,需要的朋友可以参考下...2020-06-25
  • C++ Eigen库计算矩阵特征值及特征向量

    这篇文章主要为大家详细介绍了C++ Eigen库计算矩阵特征值及特征向量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
  • C++ pair的用法实例详解

    这篇文章主要介绍了C++ pair的用法实例详解的相关资料,需要的朋友可以参考下...2020-04-25
  • VSCode C++多文件编译的简单使用方法

    这篇文章主要介绍了VSCode C++多文件编译的简单使用方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-29
  • C++中的循环引用

    虽然C++11引入了智能指针的,但是开发人员在与内存的斗争问题上并没有解放,如果我门实用不当仍然有内存泄漏问题,其中智能指针的循环引用缺陷是最大的问题。下面通过实例代码给大家介绍c++中的循环引用,一起看看吧...2020-04-25
  • C++随机点名生成器实例代码(老师们的福音!)

    这篇文章主要给大家介绍了关于C++随机点名生成器的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • C#异常处理中try和catch语句及finally语句的用法示例

    这篇文章主要介绍了C#异常处理中try和catch语句及finally语句的用法示例,finally语句的使用涉及到了C#的垃圾回收特性,需要的朋友可以参考下...2020-06-25
  • C++如何删除map容器中指定值的元素详解

    map容器是C++ STL中的重要一员,删除map容器中value为指定元素的问题是我们经常与遇到的一个问题,下面这篇文章主要给大家介绍了关于利用C++如何删除map容器中指定值的元素的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。...2020-04-25
  • C++ 约瑟夫环问题案例详解

    这篇文章主要介绍了C++ 约瑟夫环问题案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...2021-08-15
  • C++中cin的用法详细

    这篇文章主要介绍了C++中cin的用法详细,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25