iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++中的异常实例详解
  • 304
分享到

C++中的异常实例详解

2024-04-02 19:04:59 304人浏览 泡泡鱼
摘要

目录1. 异常1.1 C语言中处理错误的方式1.2 C语言处理错误方式的缺陷2. c++异常2.1 异常相关关键字2.2 异常的使用2.2.1 异常的抛出和匹配原则2.3 异常的重新

1. 异常

异常: 异常是面向对象与法处理错误的一种方式

1.1 C语言中处理错误的方式

  • 返回错误码 (很多api接口都会把错误码放到errno当中)
  • 终止程序 (assert终止、除零错误、段错误) [产生信号去终止进程]
  • C标准库中setjump和longjump组合

1.2 C语言处理错误方式的缺陷

  • 使用错误码时,还需要查看错误码表,去找每个错误码的含义
  • 当一个函数是通过返回值去输出数据,那么发生错误的时候很难处理 (不好区设置发生错误时返回什么)
  • 如果调用的函数栈很深时,使用错误码去一层层返回时,处理起来很麻烦
//第2点
T& operator[](int index)
{
	//问题: 当访问的下标index超出容器的范围,此时该返回什么值?
    //解决方案: 可以再传一个参数,用于获取真正的结果(当输出型参数), 返回值就用于判断是否正确的返回了. 
    //实际上还是很麻烦
}

2. C++异常

2.1 异常相关关键字

  • try: try块中放置可能会抛出异常的代码,这段代码被称为保护代码
  • catch: catch用于捕获异常,catch块中写处理异常的代码。它跟在try的后面,1个try后面可以跟多个catch
  • throw: throw用于抛出异常
try
{
    //保护代码(可能会抛出异常的代码)
}catch(ExceptionType e1)
{
    //处理异常
}catch(ExceptionType e2)
{
    //处理异常
}catch(ExceptionType eN)
{
    //处理异常
}

2.2 异常的使用

2.2.1 异常的抛出和匹配原则

  1. 异常是通过抛出对象而引发的,该对象的类型决定了应该匹配到哪个catch块
  2. 异常的抛出会去匹配在整个调用链中与该对象类型相匹配且距离最近的那个
  3. 当异常抛出后,执行流会直接跳转到整个调用链当中能catch到异常的位置处,不能catch的地方就不会执行了,所以这可能导致内存泄漏、文件流没关闭、未释放**等情况。(free、fclose、unlock未执行)
  4. catch(…)可以用来捕获任意类型对象的异常,一般用于表示"未知异常"或被用作异常的重新抛出时的接收catch块
  5. 在抛出与捕获的类型匹配上并不全都是类型相同才能匹配。我们可以使用基类去捕获 —> 抛出的派生类的对象。 //会在下面具体讲解

· 原则2:函数调用链中的异常 - 栈展开的匹配原则

  1. 查看throw是否在try的内部,如果存在能匹配到的catch语句,就跳转到catch中进行异常的处理。
  2. 如果没有匹配的catch就退出当前栈,去查找调用链当中的能够匹配到的catch
  3. 如果在main函数中都没有找到匹配的catch,则终止程序。

栈展开:上述的这个沿着调用链去逐个栈中查找匹配的catch语句的过程就是栈展开。

image-20211225134740717

//代码演示:f3()中抛出异常,该异常会被f1()捕捉,而在main函数中的catch是无法捕捉到的
void f3()
{
	throw 123;
}
void f2()
{
    f3();
}
void f1()
{
    try
    {
        f2();
        cout << "f1() not catched Exception!" << endl;
    }catch(int& err)
    {
        cout << "f1()-err: " << err << endl;
    }
}

int main()
{
    try
    {
        f1();
        cout << "main not catched Exception!" << endl;
    }catch(int& err)
    {
        cout << "main-err: " << err << endl; 
    }
    return 0;
}

image-20211225110321881

注意:

抛出异常对象后,会生成一个异常对象的拷贝(可能是一个临时对象),这个拷贝的临时对象在被catch后会被销毁。异常的执行流执行顺序:从throw抛出异常处跳转到调用链中能够匹配的catch语句中;然后执行catch块中的代码;catch执行完毕后在当前函数栈中顺序执行。(整个调用链中没有匹配的就终止程序)

2.3 异常的重新抛出

 可能会存在单个catch不能完全处理一个异常的情况,在经过一些校正处理后,我们希望将该异常交给外层调用链中的函数来处理,此时我们可以通过在catch中重新抛出异常的方式把异常传递给调用链的上层函数处理。

//在SecondThrowException()函数中我们要delete[]动态开辟(new出来)的空间,
//如果不使用异常的重新抛出的话,就会造成内存泄漏问题 (也可以使用RAII)
void FirstThrowException()
{
    throw "First throw a exception!";
}

void SecondThrowException()
{
    int* arr = new int[10];
    try
    {
        FirstThrowException();
    }catch(...)
    {
        cout << "Delete[] arr Success!" << endl;
        delete[] arr;
        throw;
    }
}

void SoluteException()
{
    try
    {
        SecondThrowException();
    }catch(const char* err)
    {
        cout << err << endl;
    }
}

int main()
{
    SoluteException();
    return 0;
}

image-20211225154404777

2.4 自定义异常体系

自定义异常体系实际上就是自己定义的一套异常管理体系,很多公司当中都会自定义自己的异常体系以便于规范的进行异常管理。它主要用到了我们在上面所说的一条规则: 我们只需要抛出派生类对象,然后捕获基类对象就可以了。这样的抛出与捕获方式非常便于异常的处理。

class MyException
{
public:
	MyException(string errmsg, int id)
		:_errmsg(errmsg),
		 _id(id)
	{}
	
	virtual string what() const = 0;	//必须放到public下才能让类外定义的成员访问到
protected:
	int _id;		//错误码
	string _errmsg;	//存放错误信息
	//list<StackInfo> _traceStack;	//存放调用链的信息
    //...
};

class CacheException : public MyException
{
public:
	CacheException(string errmsg, int id)
		:MyException(errmsg, id)
	{}

	virtual string what() const
	{
		return "CacheException!: " + _errmsg;
	}
};

class NetworkException : public MyException
{
public:
	NetworkException(string errmsg, int id)
		:MyException(errmsg, id)
	{}

	virtual string what() const
	{
		return "NetworkException!: " + _errmsg;
	}
};

class sqlException : public MyException
{
public:
	SqlException(string errmsg, int id)
		:MyException(errmsg, id)
	{}

	virtual string what() const
	{
		return "SqlException!: " + _errmsg;
	}
};

int main()
{
	try
	{
		//抛出任意的派生类对象
		throw SqlException("sql open failed", 10);
	}
    catch (const MyException& e)	//只需要捕获基类对象
	{
		cout << e.what() << endl;	//这里实际上完成了一个多态
	}
	catch (...)	//走到这里说明出现未知异常
	{
		cout << "Unknown Exception!" << endl;
	}

	return 0;
}

image-20211225153550934

2.5 异常安全

  1. 最好不要在构造函数中抛异常,构造函数是完成对象的构造和初始化的,在里面抛异常可能会导致对象构造不完整或没有完全初始化。 (可能会造成内存泄漏)
  2. 最好不要在析构函数中抛异常,析构函数是完成资源的清理工作的,在里面抛异常可能导致资源没清完就结束了函数调用,从而导致内存泄漏、句柄未关闭等问题。
  3. 注意new、fopen、lock的使用

2.6 异常规范

异常规范的指定是为了让使用者知道函数可能抛出哪些异常,用法:

void func1() throw(); //表示该函数不会抛异常
void func2() noexcept; //等价于throw()  表示该函数不会抛异常
void func3() throw(std::bad_alloc);	//表示该函数只会抛出bad_alloc的异常
void func4() throw(int, double, string); //表示该函数会抛出int/double/string类型中的某种异常

2.7 C++标准库的异常体系

C++提供了一系列标准的异常,定义在中,下面是这些异常的组织形式。

异常描述
std::exception该异常是所有标准 C++ 异常的父类。
std::bad_alloc该异常可以通过 new 抛出。
std::bad_cast该异常可以通过 dynamic_cast 抛出。
std::bad_exception这在处理 C++ 程序中无法预期的异常时非常有用。
std::bad_typeid该异常可以通过 typeid 抛出。
std::logic_error理论上可以通过读取代码来检测到的异常。
std::domain_error当使用了一个无效的数学域时,会抛出该异常。
std::invalid_argument当使用了无效的参数时,会抛出该异常。
std::length_error当创建了太长的 std::string 时,会抛出该异常。
std::out_of_range该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator。
std::runtime_error理论上不可以通过读取代码来检测到的异常。
std::overflow_error当发生数学上溢时,会抛出该异常。
std::range_error当尝试存储超出范围的值时,会抛出该异常。
std::underflow_error当发生数学下溢时,会抛出该异常。
int main()
{
	try
    {
        vector<int> v(5);
        v.at(5) = 0;	//v.at(下标) = v[下标] + 抛异常
    }
    catch(const exception& e)
    {
        cout << e.what() << endl;
    }
    catch(...)
    {
		cout << "Unknown Exception!" << endl;        
    }
}

2.8 异常的优缺点

2.8.1 优点

  1. 清晰的显示出错误信息
  2. 可以很好地解决返回值需要返回有效数据的函数 //如T& operator[ ] (int index)
  3. 在多层函数调用时发生错误,可以用直接在外层进行捕获异常
  4. 异常在很多第三方库中也有使用 //boost、gtest、gmock

2.8.2 缺点

  1. 异常会导致执行流跳转,这会使得调试分析程序时更加困难
  2. C++没有GC (垃圾回收机制),使用异常时可能导致资源泄露等异常安全问题。
  3. C++标准库的异常体系不实用
  4. C++的允许抛出任意类型的异常,在项目中不进行规范管理的话,会十分混乱。 //一般需要定义一套继承体系的异常规范

总结

到此这篇关于C++异常的文章就介绍到这了,更多相关C++异常内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++中的异常实例详解

本文链接: https://www.lsjlt.com/news/138303.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++中的异常实例详解
    目录1. 异常1.1 C语言中处理错误的方式1.2 C语言处理错误方式的缺陷2. C++异常2.1 异常相关关键字2.2 异常的使用2.2.1 异常的抛出和匹配原则2.3 异常的重新...
    99+
    2024-04-02
  • 详解c++中的异常
    目录一、什么是异常处理二、为什么需要异常处理,异常处理的基本思想三、异常出现之前处理错误的方式四、异常为什么好五、C++中使用异常时应注意的问题六、异常的基本语法1. 抛出和捕获异常...
    99+
    2024-04-02
  • java 异常的实例详解
    java 异常的实例详解1、异常的定义:程序在运行时出现不正常情况。异常的划分:Error:严重的问题,对于error一般不编写针对性的代码对其进行处理。Exception:非严重的问题,对于exception可以使用针对性的处理方式进行处...
    99+
    2023-05-31
    java 异常 ava
  • C++异常处理方式实例详解(超级详细!)
    目录1.C语言传统的处理错误的方式2. C++异常概念3. 异常的使用3.1 异常的抛出和捕获3.2 异常的重新抛出3.3异常安全 3.4 异常规范4.自定义异常体系5.C...
    99+
    2023-05-17
    c++ 异常处理 c++如何处理异常 c++捕获异常
  • C#中类的异常处理详解
    目录前言异常寄语总结前言 日常编码过程中,最重要的技能不是说你学会使用很多最新的编程技术或者做出一个高大上的系统。而是你在写代码过程中,对异常的处理,是否系统可以稳定,健壮。 对于异...
    99+
    2024-04-02
  • C++无try-catch的异常捕获示例详解
    目录try-catch没有try-catch的日子goto 是什么?不同函数之间跳转setjmp 和 longjmpsetjmplongjmp例子无try-catch的异常捕获try...
    99+
    2022-12-08
    C++无try-catch异常捕获 C++ 异常捕获
  • C#中类的异常处理实例分析
    今天小编给大家分享一下C#中类的异常处理实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。前言日常编码过程中,最重要的技...
    99+
    2023-06-29
  • c++中异常的示例分析
    这篇文章主要介绍了c++中异常的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、什么是异常处理一句话:异常处理就是处理程序中的错误。二、为什么需要异常处理,异常处理...
    99+
    2023-06-15
  • Android 全局异常捕获实例详解
    Android 全局异常捕获今天就来说说作为程序猿的我们每天都会遇到的东西bug,出bug不可怕可怕的是没有出bug时的堆栈信息,那么对于bug的信息收集就显得尤为重要了,一般用第三方bugly或者友盟等等都能轻易收集,但是由于公司不让使用...
    99+
    2023-05-31
    android 全局 异常捕获
  • python异常的捕捉和补救实例详解
    1、捕捉特定异常 第一个符合条件的except语句会被执行,用于报告错误。如果仅仅是报告错误,程序依然会停止。 a = 0 try: b = 5/a except Zer...
    99+
    2024-04-02
  • Java java.lang.InstantiationException异常案例详解
      java.lang.InstantiationException 是指不能实例化某个对象,一般在我们使用java反射机制去创建某个对象的时候实例化到了一个抽象类或者接口...
    99+
    2024-04-02
  • C++11中异常处理机制详解
    目录一、异常的引入二、C++异常的关键字三、异常的抛出与处理规则四、异常缺陷的处理五、自定义异常体系六、异常规范七、异常安全八、异常的优缺点1.优点2.缺点一、异常的引入 传统的C语...
    99+
    2024-04-02
  • C++中异常处理问题的详细解析
    C++中异常处理问题的详细解析引言:异常处理是现代编程语言中一个非常重要的概念,能够提供程序的健壮性和可读性。C++是一门强大的编程语言,也提供了异常处理的机制。本文将详细解析C++中的异常处理问题,并提供具体的代码示例。一、异常处理的概念...
    99+
    2023-10-22
    C++ 详细解析
  • C++异常处理noexcept正确使用示例详解
    目录C++ 异常处理使用noexcept代替throw用noexcept来解决移动构造问题noexcept和throw()哪些类函数默认使用了noexcept使用noexcept的时...
    99+
    2023-05-17
    C++ 异常处理noexcept C++ 异常处理
  • SpringBoot实战之处理异常案例详解
    前段时间写了一篇关于实现统一响应信息的博文,根据文中实战操作,能够解决正常响应的一致性,但想要实现优雅响应,还需要优雅的处理异常响应,所以有了这篇内容。 作为后台服务,能够正确的处理...
    99+
    2024-04-02
  • Java:详解Java中的异常
    目录Java异常常见异常throw和throws的区别final、finally、finalize的区别总结Java异常 Java中的异常:又称例外,是一个在程序执行期间发生的事件,...
    99+
    2024-04-02
  • Android BadTokenException异常解决案例详解
    目录解决办法1解决方法2总结 线上出现了如上的 crash,第一解决反应是在 show dialog 之前做个 isFinish 和 isDestroyed 判断,当我翻开代码正要解...
    99+
    2024-04-02
  • Java ConcurrentModificationException异常解决案例详解
    Java ConcurrentModificationException异常原因和解决方法   在前面一篇文章中提到,对Vector、ArrayList在迭代的时候如果同时对其进行修...
    99+
    2024-04-02
  • C++中异常处理问题详细解析
    C++中异常处理问题详细解析异常处理是现代编程语言中一个非常重要的概念,它可以帮助程序员有效地处理和响应运行时错误。在C++中,异常处理机制提供了一种结构化的方式来捕捉并处理异常,从而使程序能够在遇到错误时进行适当的处理,而不是直接崩溃。异...
    99+
    2023-10-22
    C++ 异常处理 问题解析
  • 详解C++中的异常和错误处理机制
    目录什么是异常处理C++中的异常处理机制什么是错误处理C++中的错误处理机制结论什么是异常处理 异常处理是指在程序执行过程中发生异常或错误时,程序能够捕获并处理这些异常或错误的机制。...
    99+
    2023-05-19
    C++异常处理机制 C++异常处理 C++错误处理机制 C++ 错误处理
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作