Я пытаюсь написать класс исключений, который нужно выбросить при сбое системного вызова. Исключение должно содержать сообщение разработчика и код errno, и 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 destructor объявляется как виртуальный и не имеет никакого эффекта броска.
virtual ~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(); //since the base class std::exception has a destrutor with no throw, the child should also follow the same, and again in this case compile time error is received: format.cpp:25: 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 должен определить деструктор следующим образом:
~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;
}
Добавление деструктора - это как выполнение обязанностей ваших действий. Деструктор можно использовать для очистки памяти кучи, назначенной в классе. Отсутствие деструктора приведет к компилятору для обеспечения неявного деструктора, что может быть причиной проблем в конкретных случаях.
Ошибка компиляции не будет получена, если класс в обсуждении имеет примитивные типы данных.
format
компилятор не создает деструктор для моего класса исключений, поскольку другие члены не являются классами и, следовательно, не должны быть уничтожены. Как только я добавляю первый член, который также является классом, компилятор автоматически создает деструктор для моего исключения, даже если я не определяю его сам. Деструктор, сгенерированный автоматически компилятором, не имеет природы «не выбрасывать», в отличие от деструктора-предка. Я прав?
fmt
статическим, так как он может использоваться всеми экземплярамиSystemError
и выполнять форматирование внутриwhat()
.