C++异常学习

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

传统的错误处理机制:

  1. 终止程序,如assert,缺陷:用户难以接受。如发生内存错误,除0错误时就会终止程序。
  2. 返回错误码,缺陷:需要程序员自己去查找对应的错误。如系统的很多库的接口函数都是通
    过把错误码放到errno中,表示错误
    实际中C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的
    错误。

C++异常概念

异常是一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的
直接或间接的调用者处理这个错误。
throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
catch: 在您想要处理问题的地方,通过异常处理程序捕获异常.catch 关键字用于捕获异
常,可以有多个catch进行捕获。
try: try 块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个 catch 块。
如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛
出异常的代码,try 块中的代码被称为保护代码。使用 try/catch 语句的语法如下所示:

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

异常的使用

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

  1. 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。
  2. 被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。
  3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,
    所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似
    于函数的传值返回)
  4. catch(…)可以捕获任意类型的异常,问题是不知道异常错误是什么。
  5. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,
    使用基类捕获,这个在实际中非常实用,我们后面会详细讲解这个。

在函数调用链中异常栈展开匹配原则
6. 首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则
调到catch的地方进行处理。
7. 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch。
8. 如果到达main函数的栈,依旧没有匹配的,则终止程序。上述这个沿着调用链查找匹配的
catch子句的过程称为栈展开。所以实际中我们最后都要加一个catch(…)捕获任意类型的异
常,否则当有异常没捕获,程序就会直接终止。
9. 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。
在这里插入图片描述
看代码

#include<iostream>
using namespace std;
int divs(int m, int n)
{if (n == 0){throw"被除数不能为0";}return m / n;
}void func1(int m ,int n )
{divs(m, n);
}void func2(int m, int n)
{func1(m, n);
}void func3(int m, int n)
{func2(m, n);
}int main()
{try{int m, n;cin >> m >> n;func3(m, n);}catch (const char* s){cout << s << endl;}catch (...){cout << 11 << endl;}return 0;
}

在这里插入图片描述
很高兴,这里学到了一个新的知识点,之前学的太马虎了

#include<iostream>
#include<string>
using namespace std;
int divs(int m, int n)
{if (n == 0){throw"被除数不能为0";}return m / n;
}void func1(int m ,int n )
{divs(m, n);
}void func2(int m, int n)
{func1(m, n);
}void func3(int m, int n)
{func2(m, n);
}int main()
{try{int m, n;cin >> m >> n;func3(m, n);}catch (string s){cout << s << endl;}catch (...){cout << 11 << endl;}return 0;
}

用下面的代捕获就不行,就不知道原因,我throw不就是一个字符串吗,为什么不能接受。后来发现,throw抛出的是字符串常量,跟string类型确实不一样。我们创建一个string是怎么样的啊?
string s =“ssss”;
其实这里就是把字符串常量变成string类型。
下面是chat老师的解释

你的代码中使用 throw “被除数不能为0”; 时,抛出的异常是一个字符串常量,其类型是 const char* 而不是 std::string。因此,在 catch 块中,你应该捕获 const char* 类型的异常而不是 std::string。

如果你想要用 std::string 来捕获这个异常,你应该在抛出异常时使用 std::string 类型,像这样:throw std::string(“被除数不能为0”);。

字符串常量跟string有什么区别呢
ChatGPT
字符串常量和 std::string 类型在 C++ 中有几个主要的区别,这些区别涉及它们的类型定义、内存管理、使用方式和功能。这里是一些关键区别:

  1. 类型和存储
    字符串常量(如 “hello”)是由编译器在编译时处理的,它们实际上是 const char[] 类型,即一个以 null (‘\0’) 字符结束的字符数组。这些字符串存储在程序的只读数据段中,通常不能被修改。
    std::string 是 C++ 标准库中定义的一个类,用于处理文本数据。它封装了动态数组的管理,可以容纳可变长度的字符串,并提供了广泛的成员函数来进行字符串操作。std::string 对象通常存储在堆上,或者在小字符串优化 (SSO) 情况下在栈上。
  2. 内存管理
    字符串常量 不需要用户进行内存管理,它们由编译器管理,并且通常不能被修改。
    std::string 管理它自己的内存,自动进行内存分配和释放。它支持动态改变大小,可以在运行时添加、修改或删除字符。
  3. 功能和方法
    字符串常量 基本上只能用作不变的文本。要执行任何复杂的操作(如拼接、搜索或替换),你需要使用 C 风格的字符串处理函数(如 strlen, strcpy, strcat 等)。
    std::string 提供了许多成员函数和运算符重载,使得字符串处理更加直接和安全。例如,你可以使用 + 运算符来拼接字符串,使用 [] 运算符来访问特定位置的字符,以及使用 .length() 或 .size() 来获取字符串的长度等。
  4. 安全性和方便性
    使用 字符串常量 可能会涉及到指针操作,这在不正确使用的情况下可能导致程序崩溃或安全问题(例如缓冲区溢出)。
    std::string 设计时考虑到安全性和方便性,自动处理内存管理,减少了缓冲区溢出的风险,并简化了代码。

查看异常捕获机制

#define  _CRT_SECURE_NO_WARNINGS#include<iostream>
using namespace std;
int divs(int m, int n)
{if (n == 0){throw"被除数不能为0";}return m / n;
}void func1(int m, int n)
{divs(m, n);
}void func2(int m, int n)
{try{func1(m, n);}catch (const char* s){cout << __LINE__ << endl;}cout << 1111 << endl;}void func3(int m, int n)
{func2(m, n);
}int main()
{try{int m, n;cin >> m >> n;func3(m, n);}catch (const char* s){cout << __LINE__ << endl;cout << s << endl;}catch (...){cout << 11 << endl;}return 0;
}

在这里插入图片描述
自定义异常类
在这里插入图片描述

#define  _CRT_SECURE_NO_WARNINGS
#include<string>
#include <time.h>
using namespace std;class Exception
{
public:Exception(const string& errmsg, int id):_errmsg(errmsg), _id(id){}virtual string what() const{return _errmsg;	}
protected:string _errmsg;int _id;
};class SqlException : public Exception
{
public:SqlException(const string& errmsg, int id, const string& sql):Exception(errmsg, id), _sql(sql){}virtual string what() const{string str = "SqlException:";str += _errmsg;str += "->";str += _sql;return str;}
private:const string _sql;
};class CacheException : public Exception
{
public:CacheException(const string& errmsg, int id):Exception(errmsg, id){}virtual string what() const{string str = "CacheException:";str += _errmsg;return str;}
};
class HttpServerException : public Exception
{
public:HttpServerException(const string& errmsg, int id, const string& type):Exception(errmsg, id), _type(type){}virtual string what() const{string str = "HttpServerException:";str += _type;str += ":";str += _errmsg;return str;}
private:const string _type;
};void HttpServer()
{// ...srand(time(0));if (rand() % 3 == 0){throw HttpServerException("请求资源不存在", 100, "get");}else if (rand() % 4 == 0){throw HttpServerException("权限不足", 101, "post");}CacheMgr();
}
void SQLMgr()
{srand(time(0));if (rand() % 7 == 0){throw SqlException("权限不足", 100, "select * from name = '张三'");}//throw "xxxxxx";
}
void CacheMgr()
{srand(time(0));if (rand() % 5 == 0){throw CacheException("权限不足", 100);}else if (rand() % 6 == 0){throw CacheException("数据不存在", 101);}SQLMgr();
}int main()
{while (1){this_thread::sleep_for(chrono::seconds(1));try {HttpServer();}catch (const Exception& e) // 这里捕获父类对象就可以{// 多态std::cout << e.what() << std::endl;}catch (...){std::cout << "Unkown Exception" << std::endl;}}return 0;
}

异常规范

  1. 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的
    后面接throw(类型),列出这个函数可能抛掷的所有异常类型。
  2. 函数的后面接throw(),表示函数不抛异常。
  3. 若无异常接口声明,则此函数可以抛掷任何类型的异常。
// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这里表示这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();
// C++11 中新增的noexcept,表示不会抛异常
thread() noexcept;
thread (thread&& x) noexcept;

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

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

相关文章

DC-8渗透测试复现

DC-8渗透测试复现 目的&#xff1a; 获取最高权限以及flag 过程&#xff1a; 信息打点--sql注入- 命令执行反弹shell-exim4提权 环境&#xff1a; 攻击机&#xff1a;kali(192.168.85.137) 靶机&#xff1a;DC_3(192.168.85.140) 复现&#xff1a; 一.信息收集 nmap -…

java泛型知多少

Java 泛型了解么&#xff1f; 泛型是一种在编译时提供类型安全检查的机制,可以增加我们代码的可读性和安全性。泛型可以在编译时期对泛型参数进行校验来指定选入对象的类型&#xff0c;比如 ArrayList<Person> persons new ArrayList<Person>() 这行代码就指明了…

包装类的认识

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x1…

小程序实现前端热更新的基础技术原理

小程序技术是一种很有前景的移动开发技术&#xff0c;尤其在移动App开发中&#xff0c;高频业务场景的热更新方向上。 时间来到了2024年&#xff0c;小程序的技术已经作为企业构建超级App的一种快速迭代业务场景&#xff0c;或者便捷引入第三方生态的技术“利器”&#xff0c;…

每日两题1

文章目录 使用最小花费爬楼梯91解码方法 使用最小花费爬楼梯 class Solution { public:int minCostClimbingStairs(vector<int>& cost) {if(cost.size() 2)return min(cost[0],cost[1]);vector<int> dp;dp.reserve(cost.size()1);dp[0] 0;dp[1] 0;for(int i…

书生·浦语大模型全链路开源体系-第4课

书生浦语大模型全链路开源体系-第4课 书生浦语大模型全链路开源体系-第4课相关资源XTuner 微调 LLMXTuner 微调小助手认知环境安装前期准备启动微调模型格式转换模型合并微调结果验证 将认知助手上传至OpenXLab将认知助手应用部署到OpenXLab使用XTuner微调多模态LLM前期准备启动…

【MySQL数据库】 (篇一 ) 让你快速上手——新手速通版

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、如何起步&#xff1f;&#x1f3c3;‍1.创建数据库&#xff1a;2.选择数据库&#xff1a;3.删除数据库&#xff1a;4.创建表&#xff1a;5.删除表&#xff…

冒泡排序的时间复杂度的讲解

冒泡排序代码&#xff1a; 冒泡排序的时间复杂度&#xff1a; 最坏情况&#xff1a; 遍历了n-1&#xff0c;n-2&#xff0c;n-3......才让数字来到该处于的位置&#xff0c;而这是一个等差数列&#xff08;n-1&#xff0c;n-2&#xff0c;n-3......&#xff09;&#xff0c;该数…

四川教育装备行业协会考察团走访云轴科技ZStack共话技术创新应用

近日&#xff0c;四川省教育装备行业协会高等教育技术专业委员会组织了一次深入的考察活动&#xff0c;旨在加强与其他省市高校及企业之间的交流与合作&#xff0c;学习借鉴先进的教育装备与管理经验&#xff0c;以提升本省的高等教育技术水平。考察团一行先后走访了武汉理工大…

2024-6.python流程控制

流程控制 流程控制指的是代码运行逻辑、分支走向、循环控制&#xff0c;是真正体现程序执行顺序的操作。 程序是由语句构成&#xff0c;而流程控制语句 是用来控制程序中每条语句执行顺序的语句。可以通过控制语句实现更丰富的逻辑以及更强大的功能。几乎所有编程语言都有流程…

Nacos源码分析,Nacos gRPC服务端设计

作为SpringCloudAlibaba微服务架构实战派上下册和RocketMQ消息中间件实战派上下册的作者&#xff0c;我来给大家分析一下Nacos的源码。 Nacos支持采用gRPC作为通信渠道&#xff0c;并且针对Nacos集群之间的通信以及客户端和Nacos服务端之间的通信&#xff0c;同时启动了两个相…

Spring (四) 之配置及配置文件的操作

文章目录 1、Spring 基于注解的配置基于注解的配置引入依赖包配置实体类数据访问层业务层业务层实现测试 2、Bean和Component和Configuration的区别1 Bean:2 Component:3 Configuration:总结&#xff1a; 区别Component和Configuration区别 3、Spring读取properties配置文件准备…