Пользовательские исключения в C ++ - PullRequest
16 голосов
/ 22 октября 2009

Я пытался создать несколько пользовательских классов исключений для библиотеки C ++, над которой я работаю. Эти пользовательские исключения собирают дополнительную информацию, такую ​​как файл, номер строки и т. Д., Необходимую для отладки, если по какой-то причине во время тестирования исключение не было обнаружено в нужном месте. Однако большинство людей, похоже, рекомендуют наследовать от класса std :: exception в STL, с чем я согласен, но мне было интересно, может быть, было бы лучше использовать множественное наследование для наследования от каждого из производного std :: exception классы (например, std :: runtime_error) и пользовательский класс исключений, как в приведенном ниже коде?

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

class Exception{
    public:
        explicit Exception(const char *origin, const char *file, 
                           const int line, const char *reason="", 
                           const int errno=0) throw();

        virtual ~Exception() throw();

        virtual const char* PrintException(void) throw();

        virtual int GetErrno(void);

    protected:
        std::string m_origin;
        std::string m_file;
        int m_line;
        std::string m_reason;
        int m_errno;
}; 

class RuntimeError: public virtual std::runtime_error, public Exception{
    public:
              explicit RuntimeError(const char *origin, const char *file, 
                                    const int line, const char *reason="", 
                                    const int errno=0) throw();
        virtual ~RuntimeError() throw();
};

Ответы [ 2 ]

16 голосов
/ 22 октября 2009

Мне было интересно, может быть, было бы лучше использовать множественное наследование для наследования от каждого из производных классов std :: exception

Обратите внимание, что это проблема, из-за того, что исключения в стандартной библиотеке происходят не виртуально друг от друга. Если вы введете множественное наследование, вы получите иерархию исключений страшного бриллианта без виртуального наследования и не сможете перехватывать производные исключения на std::exception&, поскольку ваш производный класс исключений содержит два подобъекта std::exception, что делает std::exception an " Неоднозначный базовый класс ».

Конкретный пример:

class my_exception : virtual public std::exception {
  // ...
};

class my_runtime_error : virtual public my_exception
                       , virtual public std::runtime_error {
  // ...
};

Теперь my_runtime_error выводится (косвенно) из std::exception дважды, один раз через std::run_time_error и один раз через my_exception. Поскольку первое не является производным от std::exception виртуально, это

try {
  throw my_runtime_error(/*...*/);
} catch( const std::exception& x) {
  // ...
}

не будет работать.

Изменить:

Мне кажется, я видел первый пример иерархии классов исключений с участием MI в одной из книг Страуструпа, поэтому я пришел к выводу, что в целом это хорошая идея. То, что исключения из std lib не происходят практически друг от друга, я считаю провалом.

Когда я последний раз проектировал иерархию исключений, я очень широко использовал MI, но не наследовал от классов исключений в std lib. В этой иерархии были абстрактные классы исключений, которые вы определили, чтобы ваши пользователи могли их перехватить, и соответствующие классы реализации, производные от этих абстрактных классов и от базового класса реализации, который вы на самом деле выбросили бы. Чтобы сделать это проще, я определил несколько шаблонов, которые будут выполнять всю тяжелую работу:

// something.h
class some_class {
private:
  DEFINE_TAG(my_error1); // these basically define empty structs that are needed to 
  DEFINE_TAG(my_error2); // distinguish otherwise identical instances of the exception 
  DEFINE_TAG(my_error3); // templates from each other (see below)
public:
  typedef exc_interface<my_error1>  exc_my_error1;
  typedef exc_interface<my_error2>  exc_my_error2;
  typedef exc_interface<my_error3,my_error2> // derives from the latter
                                    exc_my_error3;

  some_class(int i);
  // ...
};

//something.cpp
namespace {
  typedef exc_impl<exc_my_error1> exc_impl_my_error1;
  typedef exc_impl<exc_my_error2> exc_impl_my_error2;
  typedef exc_impl<exc_my_error3> exc_impl_my_error3;
  typedef exc_impl<exc_my_error1,exc_my_error2> // implements both
                                  exc_impl_my_error12;
}
some_class::some_class(int i)
{
  if(i < 0) 
    throw exc_impl_my_error3( EXC_INFO  // passes '__FILE__', '__LINE__' etc.
                            , /* ... */ // more info on error
                            ); 
}

Оглядываясь назад на это сейчас, я думаю, что мог бы сделать этот шаблон класса exc_impl производным от std::exception (или любого другого класса в иерархии исключений std lib, передаваемого как необязательный параметр шаблона), поскольку он никогда не наследуется от любой другой exc_impl экземпляр. Но тогда это было не нужно, поэтому мне это никогда не приходило в голову.

12 голосов
/ 22 октября 2009

Вы должны попробовать boost :: исключение

Целью Boost Exception является облегчить дизайн класса исключений иерархии и чтобы помочь написать обработка исключений и отчеты об ошибках Код.

Поддерживает транспортировку произвольных данные на сайт улова, который является в противном случае сложно из-за не броска требования (15.5.1) для исключения типы. Данные могут быть добавлены к любому объект исключения, либо непосредственно в выражение броска (15.1), или в позднее время как объект исключения распространяется вверх по стеку вызовов.

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

Boost Exception также поддерживает N2179-стиль копирования исключений объекты, реализованные ненавязчиво и автоматически Функция boost :: throw_exception.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...