【问题标题】:Using C++ Boost's format object as an exception member使用 C++ Boost 的格式对象作为异常成员
【发布时间】:2014-07-21 05:48:29
【问题描述】:

我正在尝试编写一个在系统调用失败时需要抛出的异常类。异常应该有一个开发者消息和错误代码,它的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;
};

我有几个问题:

  1. 首先 - 我是在重新发明轮子吗?是否已经有推荐的 C++ 方法来处理失败的系统调用?

  2. 为什么使用format 类作为异常成员会强制我覆盖默认析构函数?

  3. 现在我添加了自己的析构函数,有什么陷阱需要注意吗?

  4. 有没有办法只使用标准 C++ 库来实现我想要的?

【问题讨论】:

  • 我会将fmt 设为静态,因为它可以被SystemError 的所有实例共享并在what() 中进行格式化。
  • 不要为每个实例添加比绝对必要的更多的异常。不需要格式化程序。
  • @KhouriGiordano - 这是一个有趣的想法。如果我理解正确,那么它不是线程安全的。我错了吗?
  • 解析传递给格式ctor的字符串需要时间,但是制作静态的本地副本将是快速且线程安全的。现在我想了想,我不会在异常本身中进行任何格式化,仅当它通过控制台、日志文件等呈现给用户时。
  • Khouri 你是在认真谈论过早优化异常处理吗?

标签: c++ boost custom-exceptions


【解决方案1】:
  • 可以使用标准异常类或创建您自己的从 std::exception 类派生的类来处理异常。当您想要使用 std::exception 扩展已经可用的功能时,将使用继承。因此,决定完全取决于您希望如何设计您的应用程序。

  • boost::format 不会强制您覆盖析构函数。如果您注意到您是从 std::exception 派生并组成 boost::format。 std::exception 析构函数被声明为虚拟并且具有不抛出特性。

虚拟〜异常()抛出();

当你省略析构函数时,编译器隐式提供了一个析构函数,但这个析构函数没有 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 ()â

当您提供构造函数时:

〜系统错误(); //因为基类 std::exception 有一个没有 throw 的析构函数,孩子也应该遵循相同的,在这种情况下再次收到编译时错误: format.cpp:25:错误:“virtual SystemError::~SystemError()”的更宽松的抛出说明符 /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/exception:63: 错误: 覆盖 â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 成员,编译器不会为我的异常类生成析构函数,因为其他成员不是类,因此不需要被析构。一旦我添加了第一个也是类的成员,编译器就会为我的异常自动生成一个析构函数,即使我自己没有定义它。与它的祖先析构函数相反,编译器自动生成的析构函数没有“不抛出”的性质。我说的对吗?
  • 是的,您的理解是正确的。当您的类有一些需要清理的成员(boost::format 或 std::string)时,如果未提供正确的析构函数,编译器会出错。但是,如果您有原始数据类型,那么您将不会收到错误。具有 POD 类型或没有数据成员的析构函数变得微不足道 (ref en.cppreference.com/w/cpp/language/destructor)
猜你喜欢
  • 1970-01-01
  • 2023-03-27
  • 2013-09-21
  • 2013-08-18
  • 1970-01-01
  • 2021-01-12
  • 1970-01-01
  • 2017-03-21
  • 1970-01-01
相关资源
最近更新 更多