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

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

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


代码如下:

#include "stdafx.h"
#include<stdlib.h>
#include<crtdbg.h>
#include <iostream>
// 内存泄露检测机制
#define _CRTDBG_MAP_ALLOC 
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

// 自定义异常类
class MyExcepction
{
public:

// 构造函数,参数为错误代码
MyExcepction(int errorId)
{
// 输出构造函数被调用信息
std::cout << "MyExcepction is called" << std::endl;
m_errorId = errorId;
}

// 拷贝构造函数
MyExcepction( MyExcepction& myExp)
{
// 输出拷贝构造函数被调用信息
std::cout << "copy construct is called" << std::endl;
this->m_errorId = myExp.m_errorId;
}

~MyExcepction()
{
// 输出析构函数被调用信息
std::cout << "~MyExcepction is called" << std::endl;
}

// 获取错误码
int getErrorId()
{
return m_errorId;
}

private: 
// 错误码
int m_errorId;
};

int main(int argc, char* argv[])
{
// 内存泄露检测机制
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

// 可以改变错误码,以便抛出不同的异常进行测试
int throwErrorCode = 110;

std::cout << " input test code :" << std::endl;
std::cin >> throwErrorCode;

try 
{
if ( throwErrorCode == 110)
{
MyExcepction myStru(110);

// 抛出对象的地址 -> 由catch( MyExcepction* pMyExcepction) 捕获
// 这里该对象的地址抛出给catch语句,不会调用对象的拷贝构造函数
// 传地址是提倡的做法,不会频繁地调用该对象的构造函数或拷贝构造函数
// catch语句执行结束后,myStru会被析构掉
throw &myStru; 
}
else if ( throwErrorCode == 119 )
{
MyExcepction myStru(119);

// 抛出对象,这里会通过拷贝构造函数创建一个临时的对象传出给catch
// 由catch( MyExcepction myExcepction) 捕获
// 在catch语句中会再次调用通过拷贝构造函数创建临时对象复制这里传过去的对象
// throw结束后myStru会被析构掉
throw myStru; 
}
else if ( throwErrorCode == 120 )
{
// 不提倡这样的抛出方法
// 这样做的话,如果catch( MyExcepction* pMyExcepction)中不执行delete操作则会发生内存泄露

// 由catch( MyExcepction* pMyExcepction) 捕获
MyExcepction * pMyStru = new MyExcepction(120); 
throw pMyStru; 
}
else 
{
// 直接创建新对象抛出
// 相当于创建了临时的对象传递给了catch语句
// 由catch接收时通过拷贝构造函数再次创建临时对象接收传递过去的对象
// throw结束后两次创建的临时对象会被析构掉
throw MyExcepction(throwErrorCode); 

}
catch( MyExcepction* pMyExcepction)
{
// 输出本语句被执行信息
std::cout << "执行了 catch( MyExcepction* pMyExcepction) " << std::endl;

// 输出错误信息
std::cout << "error Code : " << pMyExcepction->getErrorId()<< std::endl;

// 异常抛出的新对象并非创建在函数栈上,而是创建在专用的异常栈上,不需要进行delete
//delete pMyExcepction;
}
catch ( MyExcepction myExcepction)
{
// 输出本语句被执行信息
std::cout << "执行了 catch ( MyExcepction myExcepction) " << std::endl;

// 输出错误信息
std::cout << "error Code : " << myExcepction.getErrorId()<< std::endl;
}
catch(...)
{
// 输出本语句被执行信息
std::cout << "执行了 catch(...) " << std::endl;

// 处理不了,重新抛出给上级
throw ;
}

// 暂停
int temp;
std::cin >> temp;

return 0;
}

知识点: c++异常机制

一、 概述

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

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

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

二、c++异常处理机制

C++异常处理机制是一个用来有效地处理运行错误的非常强大且灵活的工具,它提供了更多的弹性、安全性和稳固性,克服了传统方法所带来的问题.

异常的抛出和处理主要使用了以下三个关键字: try、 throw 、 catch 。

抛出异常即检测是否产生异常,在C++中,其采用throw语句来实现,如果检测到产生异常,则抛出异常。该语句的格式为:
throw 表达式;
如果在try语句块的程序段中(包括在其中调用的函数)发现了异常,且抛弃了该异常,则这个异常就可以被try语句块后的某个catch语句所捕获并处理,捕获和处理的条件是被抛弃的异常的类型与catch语句的异常类型相匹配。由于C++使用数据类型来区分不同的异常,因此在判断异常时,throw语句中的表达式的值就没有实际意义,而表达式的类型就特别重要。

try-catch语句形式如下 :


代码如下:

try 
{
包含可能抛出异常的语句;
}

catch(类型名 [形参名]) // 捕获特定类型的异常
{
}
catch(类型名 [形参名]) // 捕获特定类型的异常
{
}
catch(...) // 三个点则表示捕获所有类型的异常
{
}

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


代码如下:

#include<iostream.h> //包含头文件

#include<stdlib.h>

double fuc(double x, double y) //定义函数
{
if(y==0)
{
throw y; //除数为0,抛出异常
}
return x/y; //否则返回两个数的商
}
void main()
{
double res;
try //定义异常
{
res=fuc(2,3);
cout<<"The result of x/y is : "<<res<<endl;
res=fuc(4,0); 出现异常,函数内部会抛出异常
}
catch(double) //捕获并处理异常
{
cerr<<"error of dividing zero.\n";
exit(1); //异常退出程序
}
}

【范例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. 编写异常说明时,要确保派生类成员函数的异常说明和基类成员函数的异常说明一致,即派生类改写的虚函数的异常说明至少要和对应的基类虚函数的异常说明相同,甚至更加严格,更特殊。

(0)

相关推荐

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

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

  • C++ 异常处理 catch(...)介绍

    如果要想使一个catch block能抓获多种数据类型的异常对象的话,怎么办?C++标准中定义了一种特殊的catch用法,那就是" catch(-)". 感性认识 1.catch(-)到底是一个什么样的东东,先来个感性认识吧!看例子先: 复制代码 代码如下: int main() { try { cout << "在 try block 中, 准备抛出一个异常." << endl; //这里抛出一个异常(其中异常对象的数据类型是int,值为1

  • C++编程异常处理中try和throw以及catch语句的用法

    若要在 C++ 中实现异常处理,你可以使用 try.throw 和 catch 表达式. 首先,使用 try 块将可能引发异常的一个或多个语句封闭起来. throw 表达式发出信号,异常条件(通常是错误)已在 try 块中发生.你可以使用任何类型的对象作为 throw 表达式的操作数.该对象一般用于传达有关错误的信息.大多数情况下,建议你使用 std::exception 类或标准库中定义的派生类之一.如果其中的类不合适,建议你从 std::exception 派生自己的异常类. 若要处理可能引

  • C++中的异常处理机制详解

    异常处理 增强错误恢复能力是提高代码健壮性的最有力的途径之一,C语言中采用的错误处理方法被认为是紧耦合的,函数的使用者必须在非常靠近函数调用的地方编写错误处理代码,这样会使得其变得笨拙和难以使用.C++中引入了异常处理机制,这是C++的主要特征之一,是考虑问题和处理错误的一种更好的方式.使用错误处理可以带来一些优点,如下: 错误处理代码的编写不再冗长乏味,并且不再和正常的代码混合在一起,程序员只需要编写希望产生的代码,然后在后面某个单独的区段里编写处理错误的嗲吗.多次调用同一个函数,则只需要某个

  • 举例说明自定义C++异常处理的实例

    举例说明自定义C++异常处理的实例 例1:自定义一个继承自excepton的异常类myException C++标准中,定义在<stdexcept>中的任何异常类都派生自exception Class,本例也只是简单地由exception继承,在try段抛出一个异常并捕捉.代码如下: /*++ test.cpp version:1.0 decript:define a exception class named myException derived from base class excep

  • C++之异常处理详解

    程序中的错误分为编译时的错误和运行时的错误.编译时的错误主要是语法错误,比如:句尾没有加分号,括号不匹配,关键字错误等,这类错误比较容易修改,因为编译系统会指出错误在第几行,什么错误.而运行时的错误则不容易修改,因为其中的错误是不可预料的,或者可以预料但无法避免的,比如内存空间不够,或者在调用函数时,出现数组越界等错误.如果对于这些错误没有采取有效的防范措施,那么往往会得不到正确的运行结果,程序不正常终止或严重的会出现死机现象.我们把程序运行时的错误统称为异常,对异常处理称为异常处理.C++中所

  • C++的try块与异常处理及调试技术实例解析

    本文以示例形式简述了C++ try块的异常处理与调试技术,有助于读者复习并加深对try块的了解. 一.格式: 抛出异常throw 异常类型例如throw runtime_error("Data must refer to same ISBN"); try{ program-statements }catch(exception-specifier) { handler-statement; }catch(exception-specifier) { handler-statement;

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

    这两天我写了一个测试c++异常处理机制的例子,感觉有很好的示范作用,在此贴出来,给c++异常处理的初学者入门.本文后附有c++异常的知识普及,有兴趣者也可以看看. 下面的代码直接贴到你的console工程中,可以运行调试看看效果,并分析c++的异常机制. 复制代码 代码如下: #include "stdafx.h" #include<stdlib.h> #include<crtdbg.h> #include <iostream> // 内存泄露检测机

  • Python文件的操作示例的详细讲解

    1. 文件的读写原理: 文件的读写称为I/O操作. 操作原理: .py文件是用解释器去运行,调用OS操作系统的资源,去操作磁盘上的文件. 操作流程: 在去操作一个文件的文件的时候,首先打开或者创建一个文件,进行读或者写. 读: 从文件拿到内存当中来 写:把内存的文件放入文件中 最后进行关闭资源. 内置函数: open() 可以创建或者打开一个文件 语法规则: file=open(filename [mode,encoding] 打开模式默认为只读 默认文本中的字符编码格式为gbk # 作者:互联

  • 详解C++异常处理机制示例介绍

    目录 1.C++异常处理的套路 2.C++标准异常库中常用的几个异常处理类 2.1异常类的例子 2.2封装一个自定义异常类 1.C++异常处理的套路 C++异常处理机制之抛出异常关键字:throw throw关键字的使用: 在哪可能出现异常就在哪里使用throw关键字抛出异常. 这个异常可以使用一个常量,字符串,或类对象,都可以来抛出. throw + 常量.字符串.数对象 :用表示抛出一个异常. throw抛出的异常将层层向外抛出,至少有try....catch...语句来捕获这个异常并处理这

  • C++ Boost Optional示例超详细讲解

    目录 一.概述 二.Boost.Optional 一.概述 数据结构类似于容器,因为它们可以存储一个或多个元素.但是,它们与容器不同,因为它们不支持容器通常支持的操作.例如,使用本部分介绍的数据结构,不可能在一次迭代中访问所有元素. Boost.Optional 可以很容易地标记可选的返回值.使用 Boost.Optional 创建的对象要么是空的,要么包含单个元素.使用 Boost.Optional,您无需使用空指针或 -1 等特殊值来指示函数可能没有返回值. Boost.Tuple 提供了

  • C++ Boost Variant示例超详细讲解

    目录 一.提要 二.示例 一.提要 Boost.Variant 提供了一个类似于 union 的名为 boost::variant 的类.您可以将不同类型的值存储在 boost::variant 变量中.在任何时候只能存储一个值.分配新值时,旧值将被覆盖.但是,新值的类型可能与旧值不同.唯一的要求是这些类型必须作为模板参数传递给 boost::variant,这样它们才能为 boost::variant 变量所知. boost::variant 支持任何类型.例如,可以将 std::string

  • C++ Boost PropertyTree示例超详细讲解

    目录 一.提要 二.应用示例 练习 一.提要 借助类 boost::property_tree::ptree,Boost.PropertyTree 提供了一个树结构来存储键/值对.树形结构意味着一个树干存在许多分支,其中有许多树枝.文件系统是树结构的一个很好的例子.文件系统有一个带有子目录的根目录,这些子目录本身可以有子目录等等. 二.应用示例 要使用 boost::property_tree::ptree,请包含头文件 boost/property_tree/ptree.hpp.这是一个主头文

  • Spring MVC异常处理机制示例详解

    前言 在Spring MVC中,当一个请求发生异常(Controller抛出一个异常时), DispatcherServlet 采用委托的方式交给一个处理链来处理或者解析这个抛出的异常,这是在request和Servlet Container之间的一道屏障,所以我们可以在这里做一些处理工作,如转换异常,转换成友好的error page或者http 状态码等. 核心接口 这个处理机制在Spring是以HandlerExceptionResolver接口为核心的,该接口只有一个处理方法: Model

  • java异常处理机制示例(java抛出异常、捕获、断言)

    这是一个介绍基本异常处理的小例子,包括抛出,捕获,断言,日志. Java异常处理通过5个关键字try.catch.throw.throws.finally进行管理.基本过程是用try语句块包住要监视的语句,如果在try语句块内出现异常,则异常会被抛出,你的代码在catch语句块中可以捕获到这个异常并做处理;还有以部分系统生成的异常在Java运行时自动抛出.你也可以通过throws关键字在方法上声明该方法要抛出异常,然后在方法内部通过throw抛出异常对象. 复制代码 代码如下: package

  • java使用软引用实现缓存机制示例

    目录 正文 软引用和强引用 项目 使用idea创建一个maven项目 首先对Good实体类进行编写. 然后我们在goodbase里面编写代码,模拟一个数据库 然后书写goodscache缓存类 goodsservice模拟数据库增删改查 最后我们书写test文件 运行结果 正文 “读多写少”是大部分项目的一个特点.例如“购物”,总是看的人多(读).买的人少(写).因此,如果能减少“读”请求的次数,就能减少服务端的压力.最直接的减少“读”请求次数的方法就是使用缓存. 软引用和强引用 对于同一个读请

  • Java 超详细讲解Spring MVC异常处理机制

    目录 异常处理机制流程图 异常处理的两种方式 简单异常处理器SimpleMappingExceptionResolver 自定义异常处理步骤 本章小结 异常处理机制流程图 系统中异常包括两类: 预期异常 通过捕获异常从而获取异常信息. 运行时异常RuntimeException 主要通过规范代码开发.测试等手段减少运行时异常的发生. 系统的Dao.Service.Controller出现都通过throws Exception向上抛出,最后SpringMVC前端控制器交由异常处理器进行异常处理,如

随机推荐