27-x 第27章总结和测验
章节回顾
异常处理提供了一种机制,可以将错误或其他异常情况的处理与代码的常规控制流程解耦。这使得我们可以更灵活地选择最适合特定情况的错误处理时间和方式,从而减轻(如果不是全部)返回码带来的混乱。
throw语句用于抛出异常。try代码块会检查其内部代码(包括其调用的代码)是否抛出了异常。这些异常会被路由到catch代码块,catch 代码块会捕获特定类型的异常(如果匹配)并进行处理。默认情况下,被捕获的异常会被视为已处理。
异常会被立即处理。如果抛出异常,控制流会跳转到最近的封闭 try 代码块,查找能够处理该异常的 catch 处理程序。如果找到匹配的 try/catch 代码块,则堆栈会回溯到 catch 代码块的位置,并将控制流恢复到匹配的 catch 代码块的顶部。如果找不到 try 代码块或没有匹配的 catch 代码块,程序会调用 std::terminate,这将导致程序终止并抛出未处理的异常错误。
可以抛出任何数据类型的异常,包括类异常。
可以配置 catch 代码块来捕获特定数据类型的异常,或者使用省略号 (…) 设置一个捕获所有异常的处理程序。捕获基类引用的 catch 代码块也会捕获派生类的异常。标准库抛出的所有异常都派生自 std::exception 类(位于异常头中),因此按引用捕获 std::exception 将捕获所有标准库异常。可以使用 what() 成员函数来确定抛出的 std::exception 的类型。
在 catch 代码块内部,可能会抛出新的异常。由于这个新异常是在与该 catch 代码块关联的 try 代码块之外抛出的,因此它不会被抛出它的 catch 代码块捕获。可以使用关键字 throw 从 catch 代码块中重新抛出异常。请勿使用已捕获的异常变量重新抛出异常,否则可能会导致对象切片。
函数 try 代码块允许你捕获函数内部或关联成员初始化列表中发生的任何异常。它们通常仅用于派生类构造函数。
析构函数中绝对不应该抛出异常。
noexcept异常说明符可用于表示一个函数不会抛出异常/不会失败。
如果对象具有 noexcept 类型的移动构造函数,std::move_if_noexcept 将返回一个可移动的右值;否则,它将返回一个可复制的左值。我们可以将 noexcept 说明符与 std::move_if_noexcept 结合使用,以便在存在强异常保证时才使用移动语义(否则使用复制语义)。
最后,异常处理确实会带来开销。大多数情况下,使用异常的代码运行速度会略微降低,而且处理异常的开销非常高。你应该只在处理特殊情况时才使用异常,而不应该将其用于正常的错误处理(例如无效输入)。
章节测验
编写一个 Fraction 类,该类包含一个接受分子和分母的构造函数。如果用户传入的分母为 0,则抛出一个 std::runtime_error 类型的异常(包含在 std::except 头文件中)。在主程序中,要求用户输入两个整数。如果 Fraction 有效,则打印该分数。如果 Fraction 无效,则捕获一个 std::exception 异常,并告知用户输入的分数无效。
程序运行一次后应该输出以下内容:

解决方案:
#include <iostream>
#include <stdexcept> // for std::runtime_error
#include <exception> // for std::exception
class Fraction
{
private:
int m_numerator = 0;
int m_denominator = 1;
public:
Fraction(int numerator = 0, int denominator = 1)
: m_numerator{ numerator }
, m_denominator{ denominator }
{
if (m_denominator == 0)
throw std::runtime_error("Invalid denominator");
}
friend std::ostream& operator<<(std::ostream& out, const Fraction& f1);
};
std::ostream& operator<<(std::ostream& out, const Fraction& f1)
{
out << f1.m_numerator << '/' << f1.m_denominator;
return out;
}
int main()
{
std::cout << "Enter the numerator: ";
int numerator{};
std::cin >> numerator;
std::cout << "Enter the denominator: ";
int denominator{};
std::cin >> denominator;
try
{
Fraction f{ numerator, denominator };
std::cout << "Your fraction is: " << f << '\n';
}
catch (const std::exception& e)
{
std::cerr << e.what() << '\n';
}
return 0;
}

浙公网安备 33010602011771号