c++异常处理机制示例及讲解



      这两天我写了一个测试c++异常处理机制的例子,感觉有很好的示范作用,在此贴出来,给c++异常处理的初学者入门。本文后附有c++异常的知识普及,有兴趣者也可以看看。

     下面的代码直接贴到你的console工程中,可以运行调试看看效果,并分析c++的异常机制。
  

  1. #include "stdafx.h"  
  2. #include<stdlib.h>  
  3. #include<crtdbg.h>  
  4. #include <iostream>  
  5. // 内存泄露检测机制  
  6. #define _CRTDBG_MAP_ALLOC   
  7. #ifdef _DEBUG  
  8. #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)  
  9. #endif  
  10.  
  11. // 自定义异常类  
  12. class MyExcepction  
  13. {  
  14. public:  
  15.  
  16.         // 构造函数,参数为错误代码  
  17.         MyExcepction(int errorId)  
  18.         {  
  19.          // 输出构造函数被调用信息  
  20.             std::cout << "MyExcepction is called" << std::endl;  
  21.             m_errorId = errorId;  
  22.         }  
  23.  
  24.         // 拷贝构造函数  
  25.         MyExcepction( MyExcepction& myExp)  
  26.         {  
  27.          // 输出拷贝构造函数被调用信息  
  28.             std::cout << "copy construct is called" << std::endl;  
  29.             this->m_errorId = myExp.m_errorId;  
  30.         }  
  31.  
  32.        ~MyExcepction()  
  33.         {  
  34.             // 输出析构函数被调用信息  
  35.             std::cout << "~MyExcepction is called" << std::endl;  
  36.         }  
  37.  
  38.        // 获取错误码  
  39.         int getErrorId()  
  40.         {  
  41.             return m_errorId;  
  42.         }  
  43.  
  44. private:      
  45.         // 错误码  
  46.         int m_errorId;  
  47. };  
  48.  
  49. int main(int argc, char* argv[])  
  50. {  
  51.         // 内存泄露检测机制  
  52.         _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );  
  53.  
  54.         // 可以改变错误码,以便抛出不同的异常进行测试  
  55.         int throwErrorCode = 110;  
  56.  
  57.        std::cout << " input test code :" << std::endl;  
  58.        std::cin >> throwErrorCode;  
  59.  
  60.        try 
  61.        {  
  62.             if ( throwErrorCode == 110)  
  63.             {  
  64.              MyExcepction myStru(110);  
  65.  
  66.                 // 抛出对象的地址 -> 由catch( MyExcepction*    pMyExcepction) 捕获  
  67.                 // 这里该对象的地址抛出给catch语句,不会调用对象的拷贝构造函数  
  68.                 // 传地址是提倡的做法,不会频繁地调用该对象的构造函数或拷贝构造函数  
  69.                 // catch语句执行结束后,myStru会被析构掉  
  70.                 throw    &myStru;      
  71.             }  
  72.             else if ( throwErrorCode == 119 )  
  73.             {  
  74.              MyExcepction myStru(119);  
  75.  
  76.                 // 抛出对象,这里会通过拷贝构造函数创建一个临时的对象传出给catch  
  77.                 // 由catch( MyExcepction    myExcepction) 捕获  
  78.                 // 在catch语句中会再次调用通过拷贝构造函数创建临时对象复制这里传过去的对象  
  79.                 // throw结束后myStru会被析构掉  
  80.                 throw    myStru;      
  81.              }  
  82.              else if ( throwErrorCode == 120 )  
  83.              {  
  84.                   // 不提倡这样的抛出方法  
  85.                   // 这样做的话,如果catch( MyExcepction*    pMyExcepction)中不执行delete操作则会发生内存泄露  
  86.  
  87.                   // 由catch( MyExcepction*    pMyExcepction) 捕获  
  88.                   MyExcepction * pMyStru = new MyExcepction(120);   
  89.                   throw pMyStru;      
  90.              }  
  91.              else 
  92.              {  
  93.                   // 直接创建新对象抛出  
  94.                   // 相当于创建了临时的对象传递给了catch语句  
  95.                   // 由catch接收时通过拷贝构造函数再次创建临时对象接收传递过去的对象  
  96.                   // throw结束后两次创建的临时对象会被析构掉  
  97.                    throw MyExcepction(throwErrorCode);      
  98.              }      
  99.         }  
  100.         catch( MyExcepction*    pMyExcepction)  
  101.         {  
  102.              // 输出本语句被执行信息  
  103.                std::cout << "执行了 catch( MyExcepction*    pMyExcepction) " << std::endl;  
  104.  
  105.              // 输出错误信息  
  106.                std::cout << "error Code : " << pMyExcepction->getErrorId()<< std::endl;  
  107.  
  108.             // 异常抛出的新对象并非创建在函数栈上,而是创建在专用的异常栈上,不需要进行delete  
  109.             //delete pMyExcepction;  
  110.         }  
  111.         catch ( MyExcepction myExcepction)  
  112.         {  
  113.             // 输出本语句被执行信息  
  114.             std::cout << "执行了 catch ( MyExcepction myExcepction) " << std::endl;  
  115.  
  116.             // 输出错误信息  
  117.             std::cout << "error Code : " << myExcepction.getErrorId()<< std::endl;  
  118.         }  
  119.         catch(...)  
  120.         {  
  121.              // 输出本语句被执行信息  
  122.              std::cout << "执行了 catch(...) " << std::endl;  
  123.  
  124.              // 处理不了,重新抛出给上级  
  125.              throw ;  
  126.         }  
  127.  
  128.         // 暂停  
  129.         int temp;  
  130.         std::cin >> temp;  
  131.  
  132.        return 0;  


知识点: c++异常机制

一、 概述

C++自身有着非常强的纠错能力,发展到如今,已经建立了比较完善的异常处理机制。C++的异常情况无非两种,一种是语法错误,即程序中出现了错误的语句,函数,结构和类,致使编译程序无法进行。另一种是运行时发生的错误,一般与算法有关。

关于语法错误,不必多说,写代码时心细一点就可以解决。C++编译器的报错机制可以让我们轻松地解决这些错误。

第二种是运行时的错误,常见的有文件打开失败、数组下标溢出、系统内存不足等等。而一旦出现这些问题,引发算法失效、程序运行时无故停止等故障也是常有的。这就要求我们在设计软件算法时要全面。比如针对文件打开失败的情况,保护的方法有很多种,最简单的就是使用“return”命令,告诉上层调用者函数执行失败;另外一种处理策略就是利用c++的异常机制,抛出异常。
   
二、c++异常处理机制

    C++异常处理机制是一个用来有效地处理运行错误的非常强大且灵活的工具,它提供了更多的弹性、安全性和稳固性,克服了传统方法所带来的问题.
   
    异常的抛出和处理主要使用了以下三个关键字: try、 throw 、 catch 。
  
    抛出异常即检测是否产生异常,在C++中,其采用throw语句来实现,如果检测到产生异常,则抛出异常。该语句的格式为:
    throw 表达式;
    如果在try语句块的程序段中(包括在其中调用的函数)发现了异常,且抛弃了该异常,则这个异常就可以被try语句块后的某个catch语句所捕获并处理,捕获和处理的条件是被抛弃的异常的类型与catch语句的异常类型相匹配。由于C++使用数据类型来区分不同的异常,因此在判断异常时,throw语句中的表达式的值就没有实际意义,而表达式的类型就特别重要。
 
try-catch语句形式如下 :

  1. try 
  2. {  
  3.         包含可能抛出异常的语句;  
  4. }  
  5. catch(类型名 [形参名]) // 捕获特定类型的异常  
  6. {  
  7.  
  8. }  
  9. catch(类型名 [形参名]) // 捕获特定类型的异常  
  10. {  
  11.  
  12. }  
  13. catch(...)    // 三个点则表示捕获所有类型的异常  
  14. {  


【范例1】处理除数为0的异常。该范例将上述除数为0的异常可以用try/catch语句来捕获异常,并使用throw语句来抛出异常,从而实现异常处理,实现代码如代码清单1-1所示。
// 代码清单1-1

  1. #include<iostream.h>     //包含头文件  
  2. #include<stdlib.h>  
  3.  
  4. double fuc(double x, double y) //定义函数  
  5. {  
  6.     if(y==0)  
  7.     {  
  8.         throw y;     //除数为0,抛出异常  
  9.     }  
  10.     return x/y;     //否则返回两个数的商  
  11. }  
  12.  
  13. void main()  
  14. {  
  15.     double res;  
  16.     try  //定义异常  
  17.     {  
  18.         res=fuc(2,3);  
  19.         cout<<"The result of x/y is : "<<res<<endl;  
  20.         res=fuc(4,0); 出现异常,函数内部会抛出异常  
  21.     }  
  22.     catch(double)             //捕获并处理异常  
  23.     {  
  24.          cerr<<"error of dividing zero.\n";  
  25.          exit(1);                //异常退出程序  
  26.     }  

【范例2】自定义异常类型 (在本文开始的代码中已经给出示范)

三、异常的接口声明

为了加强程序的可读性,使函数的用户能够方便地知道所使用的函数会抛出哪些异常,可以在函数的声明中列出这个函数可能抛出的所有异常类型,例如:

void fun() throw( A,B,C,D);
这表明函数fun()可能并且只可能抛出类型(A,B,C,D)及其子类型的异常。

如果在函数的声明中没有包括异常的接口声明,则此函数可以抛出任何类型的异常,例如:
void fun();
 

一个不会抛出任何类型异常的函数可以进行如下形式的声明:
 
void fun() thow();

     
五、异常处理中需要注意的问题

1. 如果抛出的异常一直没有函数捕获(catch),则会一直上传到c++运行系统那里,导致整个程序的终止

2. 一般在异常抛出后资源可以正常被释放,但注意如果在类的构造函数中抛出异常,系统是不会调用它的析构函数的,处理方法是:如果在构造函数中要抛出异常,则在抛出前要记得删除申请的资源。

3. 异常处理仅仅通过类型而不是通过值来匹配的,所以catch块的参数可以没有参数名称,只需要参数类型。

4. 函数原型中的异常说明要与实现中的异常说明一致,否则容易引起异常冲突。
 
5. 应该在throw语句后写上异常对象时,throw先通过Copy构造函数构造一个新对象,再把该新对象传递给 catch.
       那么当异常抛出后新对象如何释放?
       异常处理机制保证:异常抛出的新对象并非创建在函数栈上,而是创建在专用的异常栈上,因此它才可以跨接多个函数而传递到上层,否则在栈清空的过程中就会被销毁。所有从try到throw语句之间构造起来的对象的析构函数将被自动调用。但如果一直上溯到main函数后还没有找到匹配的catch块,那么系统调用terminate()终止整个程序,这种情况下不能保证所有局部对象会被正确地销毁。
  
6. catch块的参数推荐采用地址传递而不是值传递,不仅可以提高效率,还可以利用对象的多态性。另外,派生类的异常扑获要放到父类异常扑获的前面,否则,派生类的异常无法被扑获。
  
7. 编写异常说明时,要确保派生类成员函数的异常说明和基类成员函数的异常说明一致,即派生类改写的虚函数的异常说明至少要和对应的基类虚函数的异常说明相同,甚至更加严格,更特殊。

本文出自 “对影成三人” 博客,请务必保留此出处http://ticktick.blog.51cto.com/823160/191881

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/352993.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql数据库char类型长度_mysql数据库设计字符类型及长度

1.数字类型小数的我就不聊了&#xff0c;因为有小数点的一般都是用字符串保存。关于整数&#xff0c;有几种可以选TINYINT、SMALLINT、MEDIUMINT、INT和BIGINT&#xff0c;分别占1、2、4、8字节。如果无符号&#xff0c;则其最大为255、65535、16777215、4294967295和184467440…

隐藏响应的server,X-Powered-By

隐藏X-Powered-By 修改 php.ini 文件 设置 expose_php Off apache 隐藏 server 修改httpd.conf 设置 ServerSignature Off ServerTokens Prod nginx 隐藏 server 修改nginx.conf 在http里面设置 server_tokens off;转载于:https://www.cnblogs.com/ouruola863/p/9519500.ht…

javaone_JavaOne 2012:掌握Java部署

javaone在为另一场JavaOne 2012午餐吃了意大利经典组合后&#xff0c;我前往希尔顿帝国宴会厅B观看了演示“掌握Java部署”。 来自Oracle的发言人是Mark Howe和Igor Nekrestyano Howe表示&#xff0c;部署团队的目标是帮助Java开发人员将其应用程序部署到所选平台。 他首先讨论…

C++异常处理机制详解

&#xfeff;&#xfeff;异常处理是一种允许两个独立开发的程序组件在程序执行期间遇到程序不正常的情况(异常exception)时相互通信的机制。本文总结了19个C异常处理中的常见问题&#xff0c;基本涵盖了一般C程序开发所需的关于异常处理部分的细节。 1. throw可以抛出哪些种类…

Github pull request 工作流总结

github 上面有很多非常不错的开源项目&#xff0c;我们也可以向其贡献自己的代码&#xff0c;那么我们如何提交自己的代码给开源项目呢&#xff1f;这里就要用到 pull request 的提交方式。当然&#xff0c;基于 git 的其他平台也是类似的用法。 假设源仓库为&#xff1a;https…

[MEGA DEAL] Java编程-硕士课程(85%折扣)

获得有关Java所有事物的高级分步指导 嘿&#xff0c;怪胎&#xff0c; 本周&#xff0c;在我们的JCG Deals商店中 &#xff0c;我们提供了一个极端的报价 。 我们提供的Java编程–硕士课程 仅售29美元&#xff0c;而不是原始价格149美元 &#xff0c;是的&#xff0c;可享受…

java中对象别名使用_JAVA中的别名现象

问题的提出&#xff1a;在java中&#xff0c;对基本数据类型的赋值时&#xff0c;是将数据从一个地方复制到另外一个地方&#xff0c;当ab时&#xff0c;将b的内容复制给a,若修改a时&#xff0c;b并不会受到这种修改的影响。在对对象进行赋值时&#xff0c;当我们对一个对象进行…

C/C++中的运算符优先级总结

C语言中的运算符 说明 运算符 结合性 初等运算符 () [] -> . -> 单目运算符 ! ~ -- - (类型) * & sizeof <- 算术运算符 * / % -> 算术运算符 - -> 移位运算符 << >> -> 关系运算符 > > < < -> 关系运算符 ! -> 按位与…

nginx 直接在配置文章中设置日志分割

直接在nginx配置文件中&#xff0c;配置日志循环&#xff0c;而不需使用logrotate或配置cron任务。需要使用到$time_iso8601 内嵌变量来获取时间。$time_iso8601格式如下&#xff1a;2015-08-07T18:12:0202:00。然后使用正则表达式来获取所需时间的数据。 按天分割日志 使用下面…

javaone_JavaOne 2012:JavaOne技术主题演讲

javaoneMark Reinhold从JavaOne 2012技术主题演讲开始。 他说&#xff0c;今年的版本将有所不同&#xff0c;因为它将使用大致相同的示例来说明Java的各个方面&#xff0c;而不是对Java的每个组件进行单独的单独介绍。 JavaFX团队的Richard Bair和Jasper Potts &#xff08;并与…

java sqlserver 死锁_sqlserver数据库发生死锁处理

SQLSERVER数据库锁表1. 查看被锁的表select request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableNamefrom sys.dm_tran_locks where resource_typeOBJECT2. 解除表的锁定declare spid intSet spid 57 --锁表进程declare sql varchar(1000)set sqlk…

setjump和longjump

int setjmp( jmp_buf env );void longjmp( jmp_buf env, int value );# setjmp(j)设置“jump”点&#xff0c;用正确的程序上下文填充jmp_buf 对象j。这个上下文包括程序存放位置、栈和框架指针&#xff0c;其它重要的寄存器和内存数据。当初始化完jump 的上下文&#xff0c;se…

jmeter操作数据库

1) jmeter不能直接连数据库&#xff0c;需要先添加jar包。 然后将jar包的路径添加到下图&#xff1a; 2) 操作数据库之前要知道数据库的信息&#xff08;ip、端口号、账号、密码&#xff09;&#xff0c;操作哪个数据库就连哪个&#xff1a; 在配置元件-JDBC Connectio…

使用Hystrix DSL创建弹性骆驼应用程序

Apache Camel是一个成熟的集成库&#xff08;到现在已有9年的历史了&#xff09;&#xff0c;它实现了Enterprise Integration Patterns一书中的所有模式。 但是Camel不仅是EIP实现库&#xff0c;它还是一个不断发展&#xff0c;添加新模式并适应行业变化的现代框架。 除了在每…

php7 对象转数组,php7中为对象/关联数组进行解构赋值

在CoffeeScript&#xff0c;Clojure&#xff0c;ES6和许多其他语言中&#xff0c;我们对对象/贴图/等进行了解构&#xff0c;如下所示&#xff1a;obj {keyA: Hello from A, keyB: Hello from B}{keyA, keyB} obj我在php中找到了这个list函数&#xff0c;可以让你像这样构造数…

虚函数表

虚函数   C中的虚函数的实现一般是通过虚函数表(C规范并没有规定具体用哪种方法&#xff0c;但大部分的编译器厂商都选择此方法)。 类的虚函数表是一块连续的内存&#xff0c;每个内存单元中记录一个JMP指令的地址。 注意的是&#xff0c;编译器会为每个有虚函数的类创建一个…

linux bash tutorial

bash read-special-keys-in-bash xdotool linux 登录启动顺序转载于:https://www.cnblogs.com/shaohef/p/9528927.html

centos7安装php8,centos8安装php7.4

一&#xff0c;下载php7.41&#xff0c;官方网站:https://www.php.net/2,下载[rootyjweb source]# wget https://www.php.net/distributions/php-7.4.2.tar.gz说明&#xff1a;在linux上以编译方式安装软件时&#xff0c;多数人都习惯把软件安装到 /usr/local目录下&#xff0c…

C++的四种强制类型转换

C的四种强制类型转换&#xff0c;所以C不是类型安全的。分别为&#xff1a;static_cast , dynamic_cast , const_cast , reinterpret_cast 为什么使用C风格的强制转换可以把想要的任何东西转换成合乎心意的类型。那为什么还需要一个新的C类型的强制转换呢&#xff1f; 新类型的…

MongoDB系列之——安装和启动

CentOS 7 安装MongoDB 4.0 社区版 1. Yum安装 创建Yum仓库先创建新的文件 vim /etc/yum.repos.d/mongodb-org-4.0.repo  在新文件中填入     [mongodb-org-4.0] nameMongoDB Repository baseurlhttps://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/…