立即前往

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
查看全部活动
热门活动
  • 智算采购季 热销S6云服务器2核4G限时88元/年起,部分主机可加赠对象存储组合包!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 一键部署Llama3大模型学习机 0代码一键部署,预装最新主流大模型Llama3与StableDiffusion
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 产品能力
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
    • 关系数据库SQL Server版
    • 企业主机安全
    • 云防火墙
    • CDN加速
    • 物理机
    • GPU云主机
    • 天翼云电脑(政企版)
    • 天翼云电脑(公众版)
    • 云主机备份
    • 弹性云主机
      搜索发现
      关系数据库SQL Server版企业主机安全云防火墙CDN加速物理机GPU云主机天翼云电脑(政企版)天翼云电脑(公众版)云主机备份弹性云主机
    • 文档
    • 控制中心
    • 备案
    • 管理中心
    • 登录
    • 免费注册

    C++异常

    首页 知识中心 软件开发 文章详情页

    C++异常

    2023-05-22 06:33:00 阅读次数:405

    c++,异常

     

    异常

    C语言传统的处理错误的方式

    传统的错误处理机制:

    1. 终止程序,如assert。缺陷:用户难以接受。如发生内存错误,除0错误时就会终止程序。
    2. 返回错误码。缺陷:需要程序员自己去查找对应的错误。如系统的很多库的接口函数都是通过把错误码放到errno中,表示错误。
    3. C标准库中setjmp和longjmp组合。(不常用)

    实际中C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的错误。

    C++异常概念

    异常是面向对象语言常用的一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数直接或间接的调用者处理这个错误。

    • throw:当程序出现问题时,可以通过throw关键字抛出一个异常。
    • try:try块中放置的是可能抛出异常的代码,该代码块在执行时将进行异常错误检测,try块后面通常跟着一个或多个catch块。
    • catch:如果try块中发生错误,则可以在catch块中定义对应要执行的代码块。

    使用try/catch语句的语法如下所示:

    try
    {
    	//被保护的代码
    }
    catch (ExceptionName e1)
    {
    	//catch块
    }
    catch (ExceptionName e2)
    {
    	//catch块
    }
    catch (ExceptionName eN)
    {
    	//catch块
    }
    

    异常的用法

    异常的抛出和捕获

    异常的抛出和捕获的匹配原则:

    1. 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码,如果抛出的异常对象没有捕获,或是没有匹配类型的捕获,那么程序会终止报错。
    2. 被选中的处理代码(catch块)是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。
    3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(类似于函数的传值返回)
    4. catch(...)可以捕获任意类型的异常,但捕获后无法知道异常错误是什么。
    5. 实际异常的抛出和捕获的匹配原则有个例外,捕获和抛出的异常类型并不一定要完全匹配,可以抛出派生类对象,使用基类进行捕获,这个在实际中非常有用。

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

    1. 当异常被抛出后,首先检查throw本身是否在try块内部,如果在则查找匹配的catch语句,如果有匹配的,则跳到catch的地方进行处理。
    2. 如果当前函数栈没有匹配的catch则退出当前函数栈,继续在上一个调用函数栈中进行查找匹配的catch。找到匹配的catch子句并处理以后,会沿着catch子句后面继续执行,而不会跳回到原来抛异常的地方。
    3. 如果到达main函数的栈,依旧没有找到匹配的catch,则终止程序。

    比如下面的代码中main函数中调用了func3,func3中调用了func2,func2中调用了func1,在func1中抛出了一个string类型的异常对象。

    void func1()
    {
    	throw string("这是一个异常");
    }
    void func2()
    {
    	func1();
    }
    void func3()
    {
    	func2();
    }
    int main()
    {
    	try
    	{
    		func3();
    	}
    	catch (const string& s)
    	{
    		cout << "错误描述:" << s << endl;
    	}
    	catch (...)
    	{
    		cout << "未知异常" << endl;
    	}
    	return 0;
    }
    

    当func1中的异常被抛出后:

    • 首先会检查throw本身是否在try块内部,这里由于throw不在try块内部,因此会退出func1所在的函数栈,继续在上一个调用函数栈中进行查找,即func2所在的函数栈。
    • 由于func2中也没有匹配的catch,因此会继续在上一个调用函数栈中进行查找,即func3所在的函数栈。
    • func3中也没有匹配的catch,于是就会在main所在的函数栈中进行查找,最终在main函数栈中找到了匹配的catch。
    • 这时就会跳到main函数中对应的catch块中执行对应的代码块,执行完后继续执行该代码块后续的代码。

    如下图所示:
    C++异常
    上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。在实际中我们最后都要加一个catch(...)捕获任意类型的异常,否则当有异常没捕获时,程序就会直接终止。

    异常的重新抛出

    有时候单个的catch可能不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理,比如最外层可能需要拿到异常进行日志信息的记录,这时就需要通过重新抛出将异常传递给更上层的函数进行处理。

    但如果直接让最外层捕获异常进行处理可能会引发一些问题。比如:

    void func1()
    {
    	throw string("这是一个异常");
    }
    void func2()
    {
    	int* array = new int[10];
    	func1();
    
    	//do something...
    
    	delete[] array;
    }
    int main()
    {
    	try
    	{
    		func2();
    	}
    	catch (const string& s)
    	{
    		cout << s << endl;
    	}
    	catch (...)
    	{
    		cout << "未知异常" << endl;
    	}
    	return 0;
    }
    

    其中func2中通过new操作符申请了一块内存空间,并且在func2最后通过delete对该空间进行了释放,但由于func2中途调用的func1内部抛出了一个异常,这时会直接跳转到main函数中的catch块执行对应的异常处理程序,并且在处理完后继续沿着catch块往后执行。

    这时就导致func2中申请的内存块没有得到释放,造成了内存泄露。这时可以在func2中先对func1抛出的异常进行捕获,捕获后先将申请到的内存释放再将异常重新抛出,这时就避免了内存泄露。比如:

    void func2()
    {
    	int* array = new int[10];
    	try
    	{
    		func1();
    		//do something...
    	}
    	catch (...)
    	{
    		delete[] array;
    		throw; //将捕获到的异常再次重新抛出
    	}
    	delete[] array;
    }
    

    说明一下:

    • func2中的new和delete之间可能还会抛出其他类型的异常,因此在fun2中最好以catch(...)的方式进行捕获,将申请到的内存delete后再通过throw重新抛出。
    • 重新抛出异常对象时,throw后面可以不用指明要抛出的异常对象(正好也不知道以catch(...)的方式捕获到的具体是什么异常对象)。

    异常安全

    将抛异常导致的安全问题叫做异常安全问题,对于异常安全问题下面给出几点建议:

    1. 构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化。
    2. 析构函数主要完成对象资源的清理,最好不要在析构函数中抛出异常,否则可能导致资源泄露(内存泄露、句柄未关闭等)。
    3. C++中异常经常会导致资源泄露的问题,比如在new和delete中抛出异常,导致内存泄露,在lock和unlock之间抛出异常导致死锁,C++经常使用RAII的方式来解决以上问题。

    异常规范

    为了让函数使用者知道某个函数可能抛出哪些类型的异常,C++标准规定:

    1. 在函数的后面接throw(type1, type2, ...),列出这个函数可能抛掷的所有异常类型。
    2. 在函数的后面接throw()或noexcept(C++11),表示该函数不抛异常。
    3. 若无异常接口声明,则此函数可以抛掷任何类型的异常。(异常接口声明不是强制的)

    比如:

    //表示func函数可能会抛出A/B/C/D类型的异常
    void func() throw(A, B, C, D);
    //表示这个函数只会抛出bad_alloc的异常
    void* operator new(std::size_t size) throw(std::bad_alloc);
    //表示这个函数不会抛出异常
    void* operator new(std::size_t size, void* ptr) throw();
    

    自定义异常体系

    实际中很多公司都会自定义自己的异常体系进行规范的异常管理。

    • 公司中的项目一般会进行模块划分,让不同的程序员或小组完成不同的模块,如果不对抛异常这件事进行规范,那么负责最外层捕获异常的程序员就非常难受了,因为他需要捕获大家抛出的各种类型的异常对象。
    • 因此实际中都会定义一套继承的规范体系,先定义一个最基础的异常类,所有人抛出的异常对象都必须是继承于该异常类的派生类对象,因为异常语法规定可以用基类捕获抛出的派生类对象,因此最外层就只需捕获基类就行了。

    如下图:
    C++异常
    最基础的异常类至少需要包含错误编号和错误描述两个成员变量,甚至还可以包含当前函数栈帧的调用链等信息。该异常类中一般还会提供两个成员函数,分别用来获取错误编号和错误描述。比如:

    class Exception
    {
    public:
    	Exception(int errid, const char* errmsg)
    		:_errid(errid)
    		, _errmsg(errmsg)
    	{}
    	int GetErrid() const
    	{
    		return _errid;
    	}
    	virtual string what() const
    	{
    		return _errmsg;
    	}
    protected:
    	int _errid;     //错误编号
    	string _errmsg; //错误描述
    	//...
    };
    

    其他模块如果要对这个异常类进行扩展,必须继承这个基础的异常类,可以在继承后的异常类中按需添加某些成员变量,或是对继承下来的虚函数what进行重写,使其能告知程序员更多的异常信息。比如:

    class CacheException : public Exception
    {
    public:
    	CacheException(int errid, const char* errmsg)
    		:Exception(errid, errmsg)
    	{}
    	virtual string what() const
    	{
    		string msg = "CacheException: ";
    		msg += _errmsg;
    		return msg;
    	}
    protected:
    	//...
    };
    class SqlException : public Exception
    {
    public:
    	SqlException(int errid, const char* errmsg, const char* sql)
    		:Exception(errid, errmsg)
    		, _sql(sql)
    	{}
    	virtual string what() const
    	{
    		string msg = "CacheException: ";
    		msg += _errmsg;
    		msg += "sql语句: ";
    		msg += _sql;
    		return msg;
    	}
    protected:
    	string _sql; //导致异常的SQL语句
    	//...
    };
    

    说明一下:

    • 异常类的成员变量不能设置为私有,因为私有成员在子类中是不可见的。
    • 基类Exception中的what成员函数最好定义为虚函数,方便子类对其进行重写,从而达到多态的效果。

    标准库异常体系

    C++标准库当中的异常也是一个基础体系,其中exception就是各个异常类的基类,我们可以在程序中使用这些标准的异常,它们之间的继承关系如下:
    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
    版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.csdn.net/chenlong_cxy/article/details/127028110,作者:2021dragon,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

    上一篇:软件开发入门教程网 之MySQL GROUP BY 语句

    下一篇:软件开发入门教程网之MySQL 索引

    相关文章

    2025-04-18 08:01:53

    Java中关于自定义异常类的一些问题

    Java中定义了大量的异常类,虽然这些异常类可以描述编程时出现的大部分异常情况,但是在程序开发中,有时还需要描述程序中特有的异常情况。为了解决这种情况,Java允许用户自定义异类。

    2025-04-18 08:01:53
    catch , throws , 异常 , 抛出 , 方法 , 自定义 , 调用
    2025-04-18 07:11:02

    Java之自定义异常(例题)

    Java之自定义异常(例题)

    2025-04-18 07:11:02
    int , 三角形 , 任意 , 异常 , 方法 , 测试
    2025-04-18 07:11:02

    Java之异常(下):自定义异常类

    Java之异常(下):自定义异常类

    2025-04-18 07:11:02
    异常 , 报错 , 方法 , 父类 , 自定义 , 赋值
    2025-04-18 07:11:02

    Java之异常(中):异常处理

    Java之异常(中):异常处理

    2025-04-18 07:11:02
    catch , try , 代码 , 处理 , 异常 , 捕获
    2025-04-18 07:11:02

    Java之异常(上):基本异常体系

    Java中的异常是指:Java程序在运行时可能出现的错误或非正常情况。

    2025-04-18 07:11:02
    Exception , 异常 , 报错 , 程序 , 输入 , 运行
    2025-04-14 09:26:51

    STL详解(九)—— priority_queue的使用与模拟实现

    优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中的元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。

    2025-04-14 09:26:51
    c++ , stl , 数据结构
    2025-04-14 09:26:51

    【算法入门08】青蛙跳台阶

    【算法入门08】青蛙跳台阶

    2025-04-14 09:26:51
    c++ , 动态规划 , 算法
    2025-04-14 09:26:51

    STL详解(八)—— stack和queue的模拟实现

    stack和queue有一点需要注意的是,虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和queue只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque容器。

    2025-04-14 09:26:51
    c++ , queue , stack , stl
    2025-04-14 09:24:23

    【算法入门14】二叉树的镜像

    【算法入门14】二叉树的镜像

    2025-04-14 09:24:23
    c++ , 算法
    2025-04-14 09:24:23

    【算法入门09】矩形覆盖

    【算法入门09】矩形覆盖

    2025-04-14 09:24:23
    c++ , 动态规划 , 算法
    查看更多
    推荐标签

    作者介绍

    天翼云小翼
    天翼云用户

    文章

    32777

    阅读量

    4817037

    查看更多

    最新文章

    Java中关于自定义异常类的一些问题

    2025-04-18 08:01:53

    Java之异常(中):异常处理

    2025-04-18 07:11:02

    Java之自定义异常(例题)

    2025-04-18 07:11:02

    Java之异常(下):自定义异常类

    2025-04-18 07:11:02

    Java之异常(上):基本异常体系

    2025-04-18 07:11:02

    golang与 C++数据结构类型对应关系是怎样的?

    2025-04-01 10:29:12

    查看更多

    热门文章

    Lambda函数

    2023-02-08 10:33:56

    QT中多线程的使用

    2023-02-07 10:34:04

    0030 简单的四则运算 c/c++

    2023-03-21 10:39:47

    C++虚函数知识点总结

    2023-02-21 06:21:46

    (10)Qt对象模型

    2023-02-13 07:55:59

    C++ 异常学习

    2023-03-15 09:25:34

    查看更多

    热门标签

    java Java python 编程开发 开发语言 代码 算法 线程 html Python 数组 C++ javascript c++ 元素
    查看更多

    相关产品

    弹性云主机

    随时自助获取、弹性伸缩的云服务器资源

    天翼云电脑(公众版)

    便捷、安全、高效的云电脑服务

    对象存储

    高品质、低成本的云上存储服务

    云硬盘

    为云上计算资源提供持久性块存储

    查看更多

    随机文章

    C++快速笔记 1.初识C++

    C/C++ realloc()函数解析

    【Python】异常处理相关练习

    以下go语言代码输出什么?

    National _C_C++_C\\试题 B: 递增序列

    CCF_201612-1 中间数(C++_数论_小根堆)

    • 7*24小时售后
    • 无忧退款
    • 免费备案
    • 专家服务
    售前咨询热线
    400-810-9889转1
    关注天翼云
    • 权益商城
    • 天翼云APP
    • 天翼云微信公众号
    服务与支持
    • 备案中心
    • 售前咨询
    • 智能客服
    • 自助服务
    • 工单管理
    • 客户公告
    • 涉诈举报
    账户管理
    • 管理中心
    • 订单管理
    • 余额管理
    • 发票管理
    • 充值汇款
    • 续费管理
    快速入口
    • 权益商城
    • 文档中心
    • 最新活动
    • 免费试用
    • 信任中心
    • 天翼云学堂
    云网生态
    • 甄选商城
    • 渠道合作
    • 云市场合作
    了解天翼云
    • 关于天翼云
    • 天翼云APP
    • 服务案例
    • 新闻资讯
    • 联系我们
    热门产品
    • 云电脑
    • 弹性云主机
    • 云电脑政企版
    • 天翼云手机
    • 云数据库
    • 对象存储
    • 云硬盘
    • Web应用防火墙
    • 服务器安全卫士
    • CDN加速
    热门推荐
    • 云服务备份
    • 边缘安全加速平台
    • 全站加速
    • 安全加速
    • 云服务器
    • 云主机
    • 智能边缘云
    • 应用编排服务
    • 微服务引擎
    • 共享流量包
    更多推荐
    • web应用防火墙
    • 密钥管理
    • 等保咨询
    • 安全专区
    • 应用运维管理
    • 云日志服务
    • 文档数据库服务
    • 云搜索服务
    • 数据湖探索
    • 数据仓库服务
    友情链接
    • 中国电信集团
    • 189邮箱
    • 天翼企业云盘
    • 天翼云盘
    ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
    公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
    • 用户协议
    • 隐私政策
    • 个人信息保护
    • 法律声明
    备案 京公网安备11010802043424号 京ICP备 2021034386号