Зачем нужны специальные классы исключений?

почему стандарт C++ потрудился изобрести std::exception классы? В чем их выгода? Причина, по которой я спрашиваю:

try
{
  throw std::string("boom");
}
catch (std::string str)
{
  std::cout << str << std::endl;
}

работает нормально. Позже, если мне нужно, я могу просто сделать свои собственные легкие типы "исключений". Так зачем мне беспокоиться о std::exception?

2 ответов


почему стандарт 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 является базовым классом для всех этих исключений:

exceptions hierarchy

предоставление базового класса для всех этих исключений позволяет для обработки нескольких исключений с помощью общего обработчика исключений.


если мне нужно, я могу просто сделать свои собственные легкие типы "исключений". Так зачем мне беспокоиться о std::exception?

Если вам нужен пользовательский класс исключений, продолжайте и сделайте его. Но!--0--> делает вашу работу проще, потому что она уже предоставляет множество функций, которые должен иметь хороший класс исключений. Он обеспечивает вам легкость выводить от его и overidding необходимые функции(в частности std::exception::what()) для вашего класса.
Это дает 2 Преимущества std::exception обработчик,

  • можно поймать стандартные исключения библиотеки, а также
  • исключения типа пользовательского класса исключений

изображение: http://en.cppreference.com/w/cpp/error/exception


Почему стандарт C++ потрудился изобрести классы std:: exception? В чем их выгода?

имеющих различные виды исключений позволяет поймать конкретные типы ошибок. Получение исключений из общей базы позволяет детализировать улавливание более общих или конкретных ошибок в зависимости от обстоятельств.

В C++ существующий тип система уже на месте, так что стандартизация строк ошибок не требуется, если можно явно создать исключение нужного типа в языке.

std:: exception и его производные классы существуют по двум основным причинам:

  1. стандартная библиотека должна иметь какую-то иерархию исключений для бросьте в исключительных обстоятельствах. Было бы неуместно всегда бросайте std:: string потому что у вас не было бы чистого способа к целевые конкретные виды ошибок.

  2. чтобы предоставить расширяемый интерфейс на основе класса для поставщиков библиотеки, чтобы бросить самые основные типы ошибок и обеспечить общий резерв для пользователей. Возможно, вы захотите предоставить больше метаданных ошибок, чем простой что() строка, чтобы человек, поймавший вашу ошибку, мог более разумно оправиться от нее.

    в то же время std:: exception как общее основание позволяет общему catchall более менее всеохватывающий, чем ... если пользователь заботится только об этом сообщении об ошибке.

Если все, что вы когда-либо сделать, это распечатать и выйти, то это не имеет значения, но вы можете также использовать std:: runtime_error, который наследует от std:: exception для удобства поймать.

позже, если мне нужно, я могу просто сделать свои собственные легкие типы "исключений". Так зачем мне беспокоиться? std::исключение?

Если вы наследуете от std:: runtime_error и использовать свой собственный тип ошибки, то вы можете ретроактивно добавлять метаданные ошибок без необходимости переписывать блоки catch! Напротив, если вы когда-либо меняли дизайн обработки ошибок, вам придется переписать все ваши std:: string уловы, потому что вы не можете безопасно наследовать от std:: string. Это не перспективный дизайн решение.

Если это не кажется так плохо прямо сейчас, представьте, что ваш код должен стать общим для нескольких проектов в качестве общей библиотеки с различными программистами, работающими над ним. Переход к новой версии вашей библиотеки станет болью.

Это даже не упоминает, что std:: string может создавать свои собственные исключения во время копирования, построения или доступа символов!

веб-сайт Boost имеет некоторые хорошие рекомендации по обработке исключений и классу строительство здесь.

История

Я пишу какой-то сетевой код и использую стороннего поставщика библиотека. На недопустимый ip-адрес, вводимый пользователем это библиотека создает пользовательское исключение nw:: invalid_ip производные от std:: runtime_error. nw:: invalid_ip содержит что() С ОПИСАНИЕМ сообщение об ошибке, но и incorrect_ip() адрес доставленный.

Я также использую std:: vector хранить гнезда, и я использую проверено at () вызов безопасного доступа к индексам. Я знаю, что если я звоните at () на значение за пределами std:: out_of_range бросается.

Я знаю, что другие вещи могут быть брошены, но я не знаю как справиться с ними, или что именно они могут быть.

когда я получаю nw:: invalid_ip ошибка Я открываю модальный файл с вводом поле для пользователя, заполненного недопустимым ip-адресом, чтобы они могли редактировать попробуйте еще раз.

на std:: out_of_range вопросы, я отвечаю, запустив проверку целостности на сокетах и фиксации отношения вектор / сокет, которое упало рассинхронизация.

для любой другой std:: exception проблемы я завершаю программу с помощью журнал ошибок. Наконец-то у меня есть catch(...) какие журналы "неизвестная ошибка!" и прекращает.

было бы трудно надежно сделать это только с 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;
    });
}

выход:

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?