Я пытаюсь написать класс исключений, который должен быть вызван при сбое системного вызова. Исключение должно содержать сообщение разработчика и код ошибки, и это what
Метод должен отформатировать сообщение разработчика вместе с кодом ошибки. C способ сделать форматирование snprintf
но я пытаюсь этого избежать. Я попытался определить члена класса за исключением типа std::stringstream
, Тем не менее, это не сработало, так как stringstream
имеет конструктор личных копий. В поисках альтернативы я узнал об объекте формата Boost. Я попытался использовать его и получил другую ошибку:
In file included from tun_device.cc:7:0:
system_error.h:9:7: error: looser throw specifier for ‘virtual SystemError::~SystemError()’
class SystemError : public exception
^
In file included from system_error.h:4:0,
from tun_device.cc:7:
/usr/include/c++/4.8/exception:64:13: error: overriding ‘virtual std::exception::~exception() throw ()’
virtual ~exception() _GLIBCXX_USE_NOEXCEPT;
Чтобы решить эту проблему, нужно определить мой собственный деструктор:
~SystemError() throw() {
}
Как я понимаю, эта строка указывает, что деструктор этого исключения не должен бросать никаких исключений.
Вот полный класс:
class SystemError : public exception
{
public:
int m_errno;
const char * m_message;
SystemError(int err, const char * message) :
fmt("%1%: %2%") {
fmt % message % errno;
m_errno = err;
this->m_message = message;
}
const char * what() const throw(){
return fmt.str().c_str();
}
~SystemError() throw() {
}
private:
format fmt;
};
У меня есть несколько вопросов:
Прежде всего — я изобретаю колесо? Уже есть ли рекомендуемый C ++ способ обработки сбойных системных вызовов?
Почему используется format
Класс как член исключения заставляет меня переопределить деструктор по умолчанию?
Есть ли какая-нибудь ловушка, о которой я должен знать теперь, когда я добавил свой собственный деструктор?
Есть ли способ достичь того, что я хочу, используя только стандартную библиотеку C ++?
Исключение может быть обработано с использованием стандартного класса исключений или созданием ваших собственных классов, производных от класса std :: exception. Наследование будет использоваться, когда вы захотите расширить уже доступную функциональность, доступную с использованием std :: exception. Таким образом, решение полностью зависит от того, как вы хотите разработать свое приложение.
boost :: format не заставляет вас переопределять деструктор. Если вы заметили, что вы производите от std :: exception и составляете boost :: format. Деструктор std :: exception объявлен как виртуальный и не имеет броска.
виртуальное ~ исключение () throw ();
Когда вы опускаете деструктор, компилятор неявно предоставляет деструктор, но этот деструктор не имеет природы throw (), следовательно, ошибка компиляции:
format.cpp:5: error: looser throw specifier for âvirtual SystemError::~SystemError()â
/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/exception:63: error: overriding âvirtual std::exception::~exception() throw ()â
Когда вы предоставляете конструктор, но как:
~ SystemError (); // так как базовый класс std :: exception имеет destrutor без броска, потомок также должен следовать тому же самому, и снова в этом случае получена ошибка времени компиляции: format.cpp: 25: error: свободный указатель броска для виртуального SystemError :: ~ SystemError () â /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/exception:63: ошибка: переопределение виртуального std :: исключение: : ~ exception () throw () â
Чтобы устранить ошибку, SystemError должен определить деструктор, подобный этому:
~SystemeError() throw();
Пример кода SSCCE:
#include <iostream>
#include<boost/format.hpp>
class SystemError : public std::exception
{
public:
int m_errno;
const char * m_message;
SystemError(int err, const char * message) :
fmt("%1%: %2%") {
fmt % message % err;
m_errno = err;
this->m_message = message;
}
const char * what() const throw(){
return fmt.str().c_str();
}
~SystemError() throw() {
}
// ~SystemError() {
//
// }private:
boost::format fmt;
};int main(){
return 0;
}
Добавление деструктора похоже на принятие ответственности за свои действия. Деструктор может быть использован для очистки любой памяти кучи, назначенной в классе. Отсутствие деструктора приведет к тому, что компилятор предоставит неявный деструктор, который может быть причиной проблем в определенных случаях.
Ошибка компиляции не будет получена, если обсуждаемый класс имеет примитивные типы данных.