什么是特殊异常类的需要?

Den*_*hie 7 c++ stl exception hierarchy

为什么C++标准会打扰发明std::exception类?他们的好处是什么?我的理由是:

try
{
  throw std::string("boom");
}
catch (std::string str)
{
  std::cout << str << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

工作良好.后来,如果我需要,我可以制作自己的轻量级"异常"类型.那我为什么要打扰std::exception

Alo*_*ave 11

为什么C++标准会打扰发明std::exception类?他们的好处是什么?

它提供了一个通用且一致的接口来处理标准库抛出的异常.标准库生成的所有异常都是从中继承的std::exception.

请注意,标准库api可以抛出许多不同类型的异常,举几个例子:

  • std::bad_alloc
  • std::bad_cast
  • std::bad_exception
  • std::bad_typeid
  • std::logic_error
  • std::runtime_error
  • std::bad_weak_ptr | C++11
  • std::bad_function_call | C++11
  • std::ios_base::failure | C++11
  • std::bad_variant_access | C++17

等等...
std::exception是所有这些异常的基类:

异常层次

为所有这些异常提供基类,允许您使用公共异常处理程序处理多个异常.


如果我需要,我可以制作自己的轻量级"异常"类型.那我为什么要打扰std::exception

如果您需要自定义异常类,请继续进行.但是std::exception让你的工作更容易,因为它已经提供了许多功能,这是一个很好的异常类应该具备的功能.它为您提供了易于从中获取并为您的类功能覆盖必要的功能(特别是std::exception::what()).
这为您提供了2个std::exception处理程序的优势,

  • 可以捕获标准库异常以及
  • 自定义异常类的类型的异常

图片提供:http://en.cppreference.com/w/cpp/error/exception


M2t*_*2tM 6

为什么C++标准需要发明std :: exception类?他们的好处是什么?

具有不同类型的异常允许您捕获特定类型的错误.从公共基础派生异常允许根据环境捕获更多通用或特定错误的粒度.

在C++中,现有的类型系统已经到位,因此当您可以在语言中显式创建所需类型的异常时,不需要标准化错误字符串.

std :: exception及其派生类的存在主要有两个原因:

  1. 标准库必须具有某种异常层次结构才能在特殊情况下抛出.总是抛出一个std :: string是不合适的,因为你没有干净的方法来定位特定类型的错误.

  2. 为库供应商提供可扩展的基于类的接口,以抛出最基本的错误类型并为用户提供共同的回退.您可能希望提供比简单的what()字符串更多的错误元数据,以便捕获错误的人可以更智能地从中恢复.

    同时std :: exception作为一个共同的基础允许一般的catchall不那么包罗万象......如果用户只关心那个错误信息.

如果您所做的只是打印并退出,那么它并不重要,但您也可以使用std :: runtime_error继承自std :: exception以方便catch.

后来,如果我需要,我可以制作自己的轻量级"异常"类型.那么我为什么要打扰std :: exception呢?

如果从std :: runtime_error继承并使用自己的自定义错误类型,则可以追溯性地添加错误元数据,而无需重写catch块!相反,如果您更改了错误处理设计,那么您将被迫重写所有std :: string catch,因为您无法安全地从std :: string继承.这不是一个前瞻性的设计决定.

如果现在看起来不那么糟糕,想象一下,如果你的代码在多个项目中作为共享库共享,并且各种程序员都在使用它.迁移到新版本的库会变得很痛苦.

这甚至没有提到std :: string可以在复制,构造或访问字符时抛出自己的异常!

Boost的网站在异常处理和一流的施工人员一些很好的指导方针在这里.

用户故事

我正在编写一些网络代码并使用第三方供应商的库.在用户输入的无效IP地址上,此库将引发从std :: runtime_error派生 的自定义异常nw :: invalid_ip.nw :: invalid_ip包含一个描述错误消息的what(),还包含提供的incorrect_ip()地址.

我还使用std :: vector来存储套接字,并且正在使用checked at()调用来安全地访问索引.我知道如果我在()上调用超出界限的值,则抛出std :: out_of_range.

我知道其他事情也可能被抛出,但我不知道如何处理它们,或者它们究竟是什么.

当我得到一个nw :: invalid_ip错误时,我弹出一个带有输入框的模态,用于填充无效ip地址的用户,这样他们就可以编辑它并再试一次.

对于std :: out_of_range问题,我通过对套接字运行完整性检查并修复已失去同步的向量/套接字关系来做出响应.

对于任何其他std :: exception问题,我使用错误日志终止程序.最后我有一个捕获(...)记录"未知错误!" 并终止.

仅仅抛出std :: string就很难做到这一点.

这是在不同情况下抛出的一些事情的基本示例,因此您可以使用捕获异常.

ExampleExceptions.cpp

#include <vector>
#include <iostream>
#include <functional>
#include <stdexcept>
#include <bitset>
#include <string>

struct Base1 {
    virtual ~Base1(){}
};
struct Base2 {
    virtual ~Base2(){}
};

class Class1 : public Base1 {};
class Class2 : public Base2 {};

class CustomException : public std::runtime_error {
public:
    explicit CustomException(const std::string& what_arg, int errorCode):
        std::runtime_error(what_arg),
        errorCode(errorCode){
    }
    int whatErrorCode() const {
        return errorCode;
    }
private:
    int errorCode;
};

void tryWrap(typename std::function<void()> f){
    try {
        f();
    } catch(CustomException &e) {
        std::cout << "Custom Exception: " << e.what() << " Error Code: " << e.whatErrorCode() << std::endl;
    } catch(std::out_of_range &e) {
        std::cout << "Range exception: " << e.what() << std::endl;
    } catch(std::bad_cast &e) {
        std::cout << "Cast exception: " << e.what() << std::endl;
    } catch(std::exception &e) {
        std::cout << "General exception: " << e.what() << std::endl;
    } catch(...) {
        std::cout << "What just happened?" << std::endl;
    }
}

int main(){
    Class1 a;
    Class2 b;

    std::vector<Class2> values;

    tryWrap([](){
        throw CustomException("My exception with an additional error code!", 42);
    });

    tryWrap([&](){
        values.at(10);
    });

    tryWrap([&](){
        Class2 c = dynamic_cast<Class2&>(a);
    });

    tryWrap([&](){
        values.push_back(dynamic_cast<Class2&>(a));
        values.at(1);
    });

    tryWrap([](){
        std::bitset<5> mybitset (std::string("01234"));
    });

    tryWrap([](){
        throw 5;
    });
}
Run Code Online (Sandbox Code Playgroud)

输出:

Custom Exception: My exception with an additional error code! Error Code: 42
Range exception: vector::_M_range_check
Cast exception: std::bad_cast
Cast exception: std::bad_cast
General exception: bitset::_M_copy_from_ptr
What just happened?
Run Code Online (Sandbox Code Playgroud)