当前位置:网站首页>初识exception
初识exception
2022-06-28 13:21:00 【GarryLau】
异常可抛出的类型(事实上,可以抛出任何类型):
1.基本类型及复合类型;
2.类类型
使用(类)对象作为异常抛出的好处:
1.对象的类名称可以传递信息;
2.对象可以存储信息,包括用于描述异常的字符串.
(备注:建议按const引用捕获异常)
如果存在未捕获的异常,会调用内建的terminate()函数,这个函数调用中的abort()终止程序
没有抛出列表的函数可以抛出任何异常。具有noexcept或者空抛出列表throw()的函数不能抛出任何异常。
throw是C++的关键字,这是抛出异常的唯一方法。
要想抛出异常,要么主动使用throw,要么是调用了其它包含throw的方法。
#pragma once
#include <iostream>
#include <stdexcept>
namespace test_exception {
auto testThrow() -> void {
int a = 10, b = 0;
try {
if(0 == b) {
throw std::runtime_error("divided 0");
}
else{
int c = a / b;
}
}
catch(std::runtime_error& e){
std::cerr << "exception occurs: " << e.what() << std::endl;
}
}
auto testWantThrow() -> void {
int a = 10, b = 0;
try {
int c = a / b; // try语句块里没有throw,因此不会抛出异常
}
catch(...){
std::cerr << "exception occurs." << std::endl;
}
}
auto main() -> int {
std::cout << "testing exception_errorprocess..." << std::endl;
testThrow();
testWantThrow();
std::cout << "------------------------------" << std::endl;
return 0;
}
}
输出:
1.异常示例
#pragma once
#include <stdexcept>
#include <exception>
#include <iostream>
namespace test_exception {
float divide1(float a, float b) {
if(b == 0){
//throw "divided by zero.";
//throw std::invalid_argument("Divide by zero");
//throw std::exception();
throw 5;
}
return a / b;
}
float divide2(float a, float b) noexcept/*throw()*/{
if(b == 0){
//throw "divided by zero.";
throw std::invalid_argument("std::invalid_argument, Divide by zero!");
//throw std::exception();
//throw 5;
}
return a / b;
}
#if 1
float divide3(float a, float b) throw(int, std::invalid_argument){
// 表示只可抛出int, std::invalid_argument两种类型的异常
if(b == 0){
//throw "divided by zero."; // 此种类型无法抛出
throw std::invalid_argument("std::invalid_argument, Divide by zero!"); // 可抛出此种类型
//throw std::exception(); // 此种类型无法抛出
//throw 5; // 可抛出此种类型
}
return a / b;
}
#endif
auto catchSample1() -> void {
int a = 10;
int b = 0;
try {
std::cout << divide1(a, b) << std::endl;
}
catch(const char* e){
std::cout << e << std::endl;
}
catch(const std::invalid_argument& e){
std::cout << e.what() << std::endl;
}
catch(const std::exception& e){
// 增加const属性不会影响匹配的目的,即此句与std::exception& e均可匹配std::exception类型的异常
std::cout << "+++" << e.what() << "+++" << std::endl;
}
catch(int e){
std::cout << "catching integer\n";
}
catch(...){
// 三个点时与所有异常类型匹配的通配符,可以使用这一技术确保捕获所有可能的异常
std::cout << "unknow exception" << std::endl;
}
}
auto catchSample2() -> void {
int a = 10;
int b = 0;
std::cout << "entering catchSample2()...............\n";
try {
std::cout << divide2(a, b) << std::endl;
}
catch(const std::invalid_argument& e){
std::cout << e.what() << std::endl;
}
catch(...){
std::cout << "unknow exception" << std::endl;
}
std::cout << "exiting catchSample2()...............\n";
}
auto main() -> void {
std::cout << "testing exception......" << std::endl;
catchSample1();
catchSample2();
std::cout << "--------------------------------------\n";
}
}
float divide2(float a, float b) noexcept定义成禁止抛出异常,因此当除以0(异常发生)时在catchSample2() 并不进行捕获,而是交由操作系统处理(一般是调用terminate())。
以上程序输出:
__cplusplus: 201103
testing exception......
catching integer
entering catchSample2()...............
terminate called after throwing an instance of 'std::invalid_argument'
what(): std::invalid_argument, Divide by zero!
需要注意的是对于形如上述代码中float divide3(float a, float b) throw(int, std::invalid_argument)抛出列表C++17已不再支持,因此如果使用C++17标准编译会报如下错误:error: ISO C++17 does not allow dynamic exception specifications
# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
2.异常层次结构

在类层次结构中捕获异常时需要注意:
1.利用多态捕获异常时,一定要按引用捕获。如果按值捕获异常就可能发生截断,此时丢失对象的信息。
2.catch语句应该按照限制减弱的顺序出现(特殊放在前、一般放在后)。例如如果想捕获invalid_argument异常,那么就应该将invalid_argument放在exception前面;如果invalid_argument放在exception后面那么invalid_argument永远无法执行。详见下面两个示例:
#pragma once
#include <stdexcept>
#include <exception>
#include <iostream>
namespace test_exception {
float divide(float a, float b) {
if(b == 0){
throw std::invalid_argument("Divide by zero");
//throw std::exception();
}
return a / b;
}
auto catchSample() -> void {
int a = 10;
int b = 0;
try {
std::cout << divide(a, b) << std::endl;
}
catch(const std::invalid_argument& e){
std::cout << e.what() << std::endl;
}
catch(const std::exception& e){
std::cout << "+++" << e.what() << "+++" << std::endl;
}
catch(...){
std::cout << "unknow exception" << std::endl;
}
}
auto main() -> void {
std::cout << "testing exception......" << std::endl;
catchSample();
std::cout << "--------------------------------------\n";
}
}
执行结果如下:
__cplusplus: 201703
testing exception......
Divide by zero
--------------------------------------
The end.
#pragma once
#include <stdexcept>
#include <exception>
#include <iostream>
namespace test_exception {
float divide(float a, float b) {
if(b == 0){
throw std::invalid_argument("Divide by zero");
//throw std::exception();
}
return a / b;
}
auto catchSample() -> void {
int a = 10;
int b = 0;
try {
std::cout << divide(a, b) << std::endl;
}
catch(const std::exception& e){
std::cout << "+++" << e.what() << "+++" << std::endl;
}
catch(const std::invalid_argument& e){
std::cout << e.what() << std::endl;
}
catch(...){
std::cout << "unknow exception" << std::endl;
}
}
auto main() -> void {
std::cout << "testing exception......" << std::endl;
catchSample();
std::cout << "--------------------------------------\n";
}
}
执行结果如下:
__cplusplus: 201703
testing exception......
+++Divide by zero+++
--------------------------------------
The end.
3.自定义异常类
建议从标准的exception直接或间接地继承的方式实现自定义异常类。
当某段代码抛出一个异常的时候,被抛出的值或者对象被复制,
即通过使用复制构造函数从旧对象构造新对象;复制是必须的,
因为原始对象在堆栈中的位置较高,因此可能在异常被捕获之前超出作用域(因此会被销毁,其所占的内存会被回收)。因此,如果编写的类的对象作为异常抛出,对象必须能被复制,这意味着如果动态分配了内存,必须编写析构函数、复制构造函数以及赋值运算符。P296
提示:作为异常抛出的对象至少会按值复制一次,按引用捕获异常可以避免不必要的复制
#pragma once
#include <stdexcept>
#include <string>
#include <iostream>
#include <fstream>
namespace test_exception {
class FileError : public std::runtime_error {
public:
FileError(const std::string& filename): filename_(filename), msg_(""), std::runtime_error(""){
}
virtual const char* what() const noexcept override{
return msg_.c_str();}
protected:
std::string filename_, msg_;
};
class FileOpenError : public FileError {
public:
FileOpenError(const std::string& filename): FileError(filename){
msg_ = std::string("Unable to open ") + filename_;
}
};
class FileReadError : public FileError {
public:
FileReadError(const std::string& filename, int linenumber): FileError(filename), linenum_(linenumber){
msg_ = std::string("Exception occurs in file ") + filename_ + std::string(", line ") + std::to_string(linenum_);
}
protected:
int linenum_;
};
auto main() -> void {
std::cout << "testing exception......" << std::endl;
std::string filename("../doc/daisy.txt");
std::ifstream in_file(filename);
try {
if(in_file.fail()){
throw FileOpenError(filename);
}
std::string line;
int linenum = 0;
while(std::getline(in_file, line)){
linenum++;
std::cout << line << std::endl;
}
if(!in_file.eof()){
in_file.close();
throw FileReadError(filename, linenum);
}
}
catch(const FileOpenError& e){
std::cout << "Line: " << __LINE__ << ", " << e.what() << std::endl;
}
catch (const FileReadError& e){
std::cout << "Line: " << __LINE__ << ", " << e.what() << std::endl;
}
catch(...){
std::cout << "unknow error occurs." << std::endl;
}
in_file.close();
std::cout << "--------------------------------------\n";
}
}
daisy.txt中的内容:
D:/daisy/5547758_eea9edfd54_n.jpg
D:/daisy/5673551_01d1ea993e_n.jpg
D:/daisy/5673728_71b8cb57eb.jpg
D:/daisy/5794835_d15905c7c8_n.jpg
D:/daisy/5794839_200acd910c_n.jpg
上述代码输出:
__cplusplus: 201703
testing exception......
D:/daisy/5547758_eea9edfd54_n.jpg
D:/daisy/5673551_01d1ea993e_n.jpg
D:/daisy/5673728_71b8cb57eb.jpg
D:/daisy/5794835_d15905c7c8_n.jpg
D:/daisy/5794839_200acd910c_n.jpg
--------------------------------------
The end.
Reference
1.Marc Gregoire, Nicholas A. Solter, Scott J. Kleper. C++高级编程(第2版). 清华大学出版社,2012
边栏推荐
- 为什么越来越多的用户放弃 Swagger,选择Apifox
- 华泰证券开户怎么开 怎么办理开户最安全
- How to display the server list of the electric donkey, and how to update the eMule server list
- 数据分析-启动子进化分析
- plt. Usage of savefig() and save path
- Arduino-ESP32闪存文件插件程序搭建和上传
- JS class is not just a simple syntax sugar!
- PHP抓取网页获取特定信息
- Rk3399 platform development series explanation (use part) pinctrl subsystem introduction - Video Introduction
- (original) [Maui] realize "floating action button" step by step
猜你喜欢

The press conference of Tencent cloud Database & CSDN engineer's ability lightweight certification is coming

投资98万美元的Saas项目失败了

1015. picking flowers

Successful cases of rights protection of open source projects: successful rights protection of SPuG open source operation and maintenance platform

2022年中国运维安全产品市场规模及发展趋势预测分析

从pdb源码到frame帧对象

flutter 系列之:flutter 中常用的 GridView layout 详解

2.01 backpack problem

如何在熊市中寻找机会?

新品体验:阿里云新一代本地SSD实例i4开放公测
随机推荐
G1垃圾收集器中重要的配置参数及其默认值
1015. picking flowers
The press conference of Tencent cloud Database & CSDN engineer's ability lightweight certification is coming
Arduino-ESP32闪存文件插件程序搭建和上传
align-items 与 align-content 的区别
无心剑英译朱熹《观书有感二首·其一》
China Database Technology Conference (DTCC) specially invited experts from Kelan sundb database to share
完全背包 初学篇「建议收藏」
Implementation of fruit and vegetable mall management system based on SSM
mysql数据库扫盲,你真的知道什么是数据库嘛
How to handle the safest account opening when downloading the mobile app of Huatai Securities
4年用户数破亿,孙哥带领波场再创新高
FS7022方案系列FS4059A双节两节锂电池串联充电IC和保护IC
Data analysis - promoter evolution analysis
认识启动函数,找到用户入口
Hubble数据库x某股份制商业银行:冠字号码管理系统升级,让每一张人民币都有 “身份证”
Centos7: switch MySQL users and log in to MySQL
Writing skills of resume
全志V853芯片 如何在Tina V85x平台切换sensor?
Oceanwide micro fh511 single chip microcomputer IC scheme small household appliances LED lighting MCU screen printing fh511