当前位置:网站首页>初识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
边栏推荐
- How does Quanzhi v853 chip switch sensors on Tina v85x platform?
- Centos7——安装mysql5.7
- 泛海微FH511单片机IC方案小家电LED照明MCU丝印FH511
- Centos7:切换mysql用户并登录mysql
- The difference between align items and align content
- Stackoverflow 2022 database annual survey
- 移动Web实训DAY-1
- 2. 01背包问题
- Mature case and source code of hash quiz game system development technology
- Centos6.5 php+mysql MySQL library not found
猜你喜欢

895. 最长上升子序列

pytorch基础

FH511+TP4333组成一个户外移动电源照明野营灯方案。

Elastic box auto wrap demo

嵌入式开发:估算电池寿命的7个技巧

Manjaro easyconnecy error: libgtk-x11-2.0 so. 0: cannot open shared object file: No such file or directory

真香啊!最全的 Pycharm 常用快捷键大全!

Tencent has confirmed that QQ has stolen numbers on a large scale, iphone14 has no chance of type-C, and 5g, the fourth largest operator, has officially released numbers. Today, more big news is here

Recognize the startup function and find the user entry

5A synchronous rectifier chip 20V to 12v2a/5v4.5a high current 24W high power synchronous rectifier chip high current step-down IC fs2462
随机推荐
Stm32f1 and stm32cubeide programming example - matrix keyboard driver
PHP crawls web pages for specific information
从pdb源码到frame帧对象
Data analysis - promoter evolution analysis
无心剑英译朱熹《观书有感二首·其一》
matlab plotyy 坐标轴设置,[转载]Matlab plotyy画双纵坐标图实例[通俗易懂]
Mature case and source code of hash quiz game system development technology
Hang Seng Electronics: lightdb, a financial distributed database, has passed a number of evaluations by China Academy of communications technology
php获取数字的个位数并替换为指定的尾数
4年用户数破亿,孙哥带领波场再创新高
同花顺上怎么进行开户啊, 安全吗
Commonly used "redmine" for # test bug
Scratch travel photo album Electronic Society graphical programming scratch grade examination level 1 true questions and answers analysis June 2022
G1垃圾收集器中重要的配置参数及其默认值
为什么越来越多的用户放弃 Swagger,选择Apifox
plt. Usage of savefig() and save path
align-items 与 align-content 的区别
ShareIt has outstanding strength and landed in the top 7 of the global IAP strength list
Centos7——安装mysql5.7
决策树预测红酒品质