Использование конструктора одного из базовых классов без другого базового класса, имеющего конструктор по умолчанию в множественном наследовании - PullRequest
0 голосов
/ 02 мая 2020

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

Например, в следующей иерархии, если вы перехватываете экземпляр std::logic_error, тогда также будет пойман экземпляр LogicException:

                -> Exception --> LogicException
               /                      ^
std::exception --> std::logic_error --^

Итак, вот код для достижения этого (исключая защиту заголовка, включает в себя и области пространства имен и определения):

class Exception: public std::exception
{
    public:
        Exception() = delete;
        Exception(const char *) noexcept(true);
        Exception(const std::string &) noexcept(true);

        virtual Message what() const noexcept(true);

    private:
        std::string message;
};

class LogicException: public Exception, public std::logic_error
{
    public:
        LogicException() = delete;
        using Exception::Exception;

        using Exception::what;
};

Однако, учитывая следующую основную функцию c main (опять же, исключая бессмысленные части):

int main()
{
    throw LogicException("Oops!");
}

Я получу следующую ошибку (компиляция с G CC 10.0.1):

test.cpp: In function ‘int main()’:
test.cpp:5:29: error: use of deleted function ‘LogicException::LogicException(const char*) [inherited from Exception]’
    5 |     throw LogicException("e");
      |                             ^
In file included from test.cpp:1:
./include/exception.hpp:27:34: note: ‘LogicException::LogicException(const char*) [inherited from Exception]’ is implicitly deleted because the default definition would be ill-formed:
   27 |                 using Exception::Exception;
      |                                  ^~~~~~~~~
./include/exception.hpp:27:34: error: no matching function for call to ‘std::logic_error::logic_error()’
In file included from ./include/exception.hpp:4,
                 from test.cpp:1:
/usr/include/c++/10/stdexcept:131:5: note: candidate: ‘std::logic_error::logic_error(const std::logic_error&)’
  131 |     logic_error(const logic_error&) _GLIBCXX_NOTHROW;
      |     ^~~~~~~~~~~
/usr/include/c++/10/stdexcept:131:5: note:   candidate expects 1 argument, 0 provided
/usr/include/c++/10/stdexcept:126:5: note: candidate: ‘std::logic_error::logic_error(std::logic_error&&)’
  126 |     logic_error(logic_error&&) noexcept;
      |     ^~~~~~~~~~~
/usr/include/c++/10/stdexcept:126:5: note:   candidate expects 1 argument, 0 provided
/usr/include/c++/10/stdexcept:124:5: note: candidate: ‘std::logic_error::logic_error(const char*)’
  124 |     logic_error(const char*) _GLIBCXX_TXN_SAFE;
      |     ^~~~~~~~~~~
/usr/include/c++/10/stdexcept:124:5: note:   candidate expects 1 argument, 0 provided
/usr/include/c++/10/stdexcept:120:5: note: candidate: ‘std::logic_error::logic_error(const string&)’
  120 |     logic_error(const string& __arg) _GLIBCXX_TXN_SAFE;
      |     ^~~~~~~~~~~
/usr/include/c++/10/stdexcept:120:5: note:   candidate expects 1 argument, 0 provided

Итак, вопросы:

  • Через некоторое время я думаю, что именно поэтому возникает ошибка: При использовании Exception конструктор, компилятор пытается неявно вызвать (по умолчанию) конструктор std::logic_error, который не существует. Это правда?

  • Есть ли способ предотвратить эту ошибку (например, как-то явно вызвать конструктор std::logic_error) без необходимости явного объявления конструктора LogicException или изменения иерархии классов?

    Примечание: Причина, по которой я не хочу явно объявлять LogicException::LogicException, заключается в том, что у меня есть некоторые классы исключений, определенные таким образом, поэтому я не хочу добавлять дополнительные файлы, определяющие LogicException класс или что-то еще.

  • Вообще говоря, хорошо ли использовать приведенную выше идею при разработке исключений?

Спасибо.

1 Ответ

1 голос
/ 03 мая 2020

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

Что бросить

Так как вы все равно не должны бросать исключения unspecifi c, потому что вы не сможете обрабатывать это определенным образом c на стороне вызывающего, только добавьте Speci c оставляет существующей иерархии исключений, поддерживаемых вашей платформой. Если вы программируете стандарт C ++, это иерархия std::exception.

Что ловить

Вы должны быть очень осторожны с ловлей std::exception. Я бы предложил сделать это только в main (или эквивалентную функцию верхнего уровня), чтобы записать ошибку перед выходом. Исключениями, которые вам разрешено ловить, являются те, условия которых вы понимаете и над которыми вы полностью контролируете; например, что файл не существует, для которого у программы есть альтернативы или он вообще не нужен (потому что ist просто необязательный).

what к сообщению

Как я понимаю это свойство what() - это исходное сообщение от места, где произошел «несчастный случай», оно дает дополнительную информацию, с которой ваша программа не должна связываться. [1] Это для «судебной экспертизы», в основном просто для генерации вывода (для отображения в пользовательском интерфейсе или добавления в файл журнала). Посмотрите следующую небольшую демонстрацию моего понимания:

#include <iostream>
#include <stdexcept>

class my_ex: public std::logic_error {
public:
    my_ex(const char* const what): std::logic_error(what) {}
};

void just_throw_the_message(const char* const msg) {
    throw my_ex(msg);
}

int main() {
    try {
        just_throw_the_message("I feel somewhat exceptional.");
    } catch (const std::exception& e) {
        std::cout << "exception caught: " << e.what() << std::endl;
    }
    return 0;
}

(см. Код выше ideone.com )

Заключение

Ваше первоначальное утверждение было (выделено мое):

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

Единственный разумный ответ, который я вижу на это требование, - получить ваш класс непосредственно от std::exception или одного из его потомков! Не используйте множественное наследование для исключений! В частности, , насколько это возможно , не намного больше, чем классификация ошибок, уже определенная иерархией, на которую вы строите. Поэтому в стандарте C ++ я настоятельно рекомендую наследовать (в конечном итоге) от std::exception.

Если у вас уже есть специфичная для приложения иерархия исключений c, которая соответствует вашим потребностям, просто сделайте ее базовый класс потомком std::exception.


[1] Что касается различения guish исключительных событий программно, используйте тип исключения. Также имеет смысл добавить свойства к вашей собственной программе c исключительный класс (ы).

...