一 标准异常类的成员:
① 在上述继承体系中,每个类都有提供了构造函数、复制构造函数、和赋值操作符重载。
② logic_error类及其子类、runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述
③ 所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息。
1.1 标准异常类的具体描述:
异常名称 | 描述 |
exception | 所有标准异常类的父类 |
bad_alloc | 当operator new and operator new[],请求分配内存失败时 |
bad_exception | 这是个特殊的异常,如果函数的异常抛出列表里声明了bad_exception异常,当函数内部抛出了异常抛出列表中没有的异常,这是调用的unexpected函数中若抛出异常,不论什么类型,都会被替换为bad_exception类型 |
bad_typeid | 使用typeid操作符,操作一个NULL指针,而该指针是带有虚函数的类,这时抛出bad_typeid异常 |
bad_cast | 使用dynamic_cast转换引用失败的时候 |
ios_base::failure | io操作过程出现错误 |
logic_error | 逻辑错误,可以在运行前检测的错误 |
runtime_error | 运行时错误,仅在运行时才可以检测的错误 |
1.2 logic_error的子类:
异常名称 | 描述 |
length_error | 试图生成一个超出该类型最大长度的对象时,例如vector的resize操作 |
domain_error | 参数的值域错误,主要用在数学函数中。例如使用一个负值调用只能操作非负数的函数 |
out_of_range | 超出有效范围 |
invalid_argument | 参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是’0’或’1’的时候,抛出该异常 |
1.3 runtime_error的子类:
异常名称 | 描述 |
range_error | 计算结果超出了有意义的值域范围 |
overflow_error | 算术计算上溢 |
underflow_error | 算术计算下溢 |
invalid_argument | 参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是’0’或’1’的时候,抛出该异常 |
示例代码:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <stdexcept> // 标准异常
class num_1
{
public:
int age;
num_1(int age)
{
if(age<0 || age>150)
{
throw length_error("年龄有错误");
}
}
};
int main(void)
{
try
{
num_1 p1(1112);
}
catch(exception &p2)
{
cout << "类型异常,异常类为:" << p2.what() << endl;
}
return 0;
}
在代码中,定义了一个类num_1,其中有一个构造函数,用于判断年龄是否符合要求,并抛出标准异常length_error。在主函数中,通过使用try-catch语句来捕获并处理异常。如果捕获到异常,会输出异常类的信息。通过这种方式可以处理并控制程序中的异常情况。
二 自己写一个异常类
2.1 为什么自己写异常类
① 标准库中的异常是有限的;
② 在自己的异常类中,可以添加自己的信息。(标准库中的异常类值允许设置一个用来描述异常的字符串)。
2.2 如何编写自己的异常类?
① 建议自己的异常类要继承标准异常类。因为C++中可以抛出任何类型的异常,所以我们的异常类可以不继承自标准异常,但是这样可能会导致程序混乱,尤其是当我们多人协同开发时。
② 当继承标准异常类时,应该重载父类的what函数。
2.3 异常类的编写
2.3.1 多态
因为我们自己编写的异常类要继承自 exception 这个标准异常类,所以我们应该有一种思路就是使用多态来扩展,多态的好处就是不影响原基类的情况下进行安全扩展。
我们来到 exception 类的定义:
virtual const char*
what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW;
可以看到标准异常类也为多态做好了准备,做了一个virtual的虚函数。注意这里返回的是char* 类型的数组。
2.3.2 异常类的编写
第一步先写一个继承类,继承自exception。
代码如下:
class num_1:public exception //自己写的异常类
{
private:
string msg;
public:
num_1(string msg)
{
this->msg = msg;
}
virtual const char *what() // 记得这里的返回值是char*
{
return this->msg.c_str(); // .c_str()作用是类型转换,即string——>char*
}
};
第二步做了一个普通的类,并在异常处使用 throw 抛出异常类 num_1。
代码如下:
class num_2 //普通类,进行年龄判断
{
public:
int age;
num_2(int age)
{
if(age<0 || age>160)
{
if(age<0)
{
throw num_1("年龄小于0,异常"); //抛异常
}
else
{
throw num_1("年龄大于160,异常"); //抛异常
}
}
else
{
this->age = age;
}
}
};
第三步,在 main 函数中进行。
代码如下:
try
{
num_2 p1(180);
}
catch (exception &e) // 相当于exception &e = num_1("年龄大于160,异常") 符合多态条件
{
cout << e.what() << endl;
}
总体代码:
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <string>
#include<iomanip>
#include<fstream> //头文件流
#include<vector>
using namespace std;
class num_1:public exception //自己写的异常类
{
private:
string msg;
public:
num_1(string msg)
{
this->msg = msg;
}
virtual const char *what() // 记得这里的返回值是char*
{
return this->msg.c_str(); // .c_str()作用是类型转换,即string——>char*
}
};
class num_2
{
public:
int age;
num_2(int age)
{
if(age<0 || age>160)
{
if(age<0)
{
throw num_1("年龄小于0,异常");
}
else
{
throw num_1("年龄大于160,异常");
}
}
else
{
this->age = age;
}
}
};
int main(void)
{
try
{
num_2 p1(180);
}
catch (num_1 &e) // exception &e = num_1("年龄大于160,异常")
{
cout << e.what() << endl;
}
return 0;
}
总结
- 异常类可以通过继承exception类来自定义
- 异常类可以包含自定义的错误信息,方便在异常处理时输出错误原因
- 异常类的成员函数what()必须被重写,返回错误信息的char*类型
- 在异常类的构造函数中,可以通过条件判断来抛出异常,例如判断年龄是否小于0或大于160
- 在主函数中,可以使用try-catch块来捕获并处理抛出的异常
- 捕获到的异常对象可以通过引用传递给catch块,然后使用e.what()来输出错误信息