基本的异常类
有的时候C++标准库的异常类所提供的信息不足以满足我们的需要。我们想自定义一个异常类。因此需要对异常类进行仔细地设计。C++可以把任意类型的类对象作为抛出的异常,但是异常处理的思想是提供有关错误原因的信息,因此必须尽可能地将类中的属性和方法设计的更符合程序错误处理的规律。
以一个简单的demo为例说明如何自定义异常类。
Step 1:存储错误原因。
class Myexcept
{
private:
string message;
};
Step 2:定义构造函数。构造函数的输入是一个string_view对象,作为错误原因message的初始值。
class Myexcept
{
private:
string message;
public:
Myexcept(string_view str = "A problem") : message{str} {}
};
Steps 3:提供输出的字符串原型。利用what方法可以返回错误原因message的值,用于catch块中的输出。
class Myexcept
{
private:
string message;
public:
Myexcept(string_view str = "A problem") : message{str} {}
string_view what() const { return message; }
};
注意使用C++17标准,否则不能编译通过。注意使用catch块捕获异常的时候用引用,因为若未指定为引用,则异常对象会再次复制,这是不必要的。
//完整的程序代码
#include <iostream>
#include <string>
#include <string_view>
using namespace std;
class Myexcept
{
private:
string message;
public:
Myexcept(string_view str = "A problem") : message{str} {}
string_view what() const { return message; }
};
int main()
{
try
{
throw Myexcept{};
}
catch (const Myexcept &e)
{
cerr << e.what() << 'n';
}
}
运行结果如下所示,
A problem
继承的异常类
在C++中,经常遇到多个异常具有共同的特点,这时我们可以搞一个大异常作为基类,特殊情况作为派生类。
在上面的Myexcept基础之上,在基类中增加虚拟析构函数,同时设置what函数为虚函数。这样做是因为在发生异常的时候如果使用多态性,直接析构派生类,这也是异常类继承的关键。注意把message的访问控制改为protected,以便于
class Myexcept
{
protected:
string message;
public:
Myexcept(string_view str = "A problem") : message{str} {}
virtual ~Myexcept() = default;
virtual string_view what() const { return message; }
};
派生异常类的定义没有基类那么麻烦,只需要定义一个构造函数。
class Bigexcept:public Myexcept
{
public:
Bigexcept(string_view str = "A big problem") : Myexcept{str} {}
};
在异常类的继承中很重要的是参数类型和抛出的异常类型的匹配问题。在匹配参数类型和抛出的异常类型时,出现下列类型是匹配的:
- 参数类型与异常类型完全匹配(忽略const)
- 参数类型是异常类的基类或间接基类,或是异常类的直接或间接基类引用(忽略const)
- 异常和参数都是指针,异常类型可以隐式转换为参数类型(忽略const)
下面是完整的测试代码。
#include <iostream>
#include <string>
#include <string_view>
using namespace std;
class Myexcept
{
protected:
string message;
public:
Myexcept(string_view str = "A problem") : message{str} {}
virtual ~Myexcept() = default;
virtual string_view what() const { return message; }
};
class Bigexcept:public Myexcept
{
public:
Bigexcept(string_view str = "A big problem") : Myexcept{str} {}
};
int main()
{
try
{
throw Bigexcept{};
}
catch (const Myexcept &e)
{
cerr << e.what() << 'n';
}
}