从防护类析构函数中抛出异常会导致std :: terminate

时间:2015-01-02 04:50:08

标签: c++ exception scope

我正在尝试创建一个“大多数不变”的类,允许客户端在需要时打破不变量,但只有在他们离开发生恶意的范围之前修复它。

以下是涉及的两个课程。它与范围保护相似。更多细节,评论以及对ideone的小测试。

http://ideone.com/dMCHVU

class HCAccessor;

class HasConditions
{
    // class "mostly-invariant"
    // 7 < payload_ <42
    int payload_;

    bool valid() const
    {
        if (!(7 < payload_) || !(payload_ < 42))
            return false;
        else
            return true;
    }

public:
    HasConditions(const int payload)
        : payload_(payload)
    {
        if (!valid())
        {
            throw std::runtime_error("can't construct");
        }
    }

    friend class HCAccessor;
};

class HCAccessor
{
    HasConditions& hc_;

public:
    HCAccessor(HasConditions& hc)
        : hc_(hc)
    {}

    HCAccessor(HCAccessor& other)
        : hc_(other.hc_)
    {}

    ~HCAccessor()
    {
        if (!hc_.valid())
        {
            throw std::runtime_error("you broke it!");
        }
    }

    void payload(const int newval)
    {
        hc_.payload_ = newval;
    }

    int payload() const
    {
        return hc_.payload_;
    }
};

当“大多数不变”被破坏然后修复代码似乎工作。当“大多数不变”仍然被打破且~HCAccessor()抛出时,std::terminate被调用,我不知道为什么。导致std::terminate调用的异常原因似乎都不合适。

http://en.cppreference.com/w/cpp/error/terminate

据我所知,只抛出一个异常,然后立即调用std::terminate

为什么会发生这种情况,我该如何解决?

2 个答案:

答案 0 :(得分:3)

默认情况下,C ++ 11中的析构函数为noexcept。如果你真的想这样做,你必须使用noexcept(false)声明HCAccessor的析构函数:

~HCAccessor() noexcept(false) {

或老式的掷骰子:

~HCAccessor() throw(std::runtime_error) {

(感谢Pradhan提供我之前从未见过的noexcept(false)语法。我认为这不是经常需要的。

然而,这样做几乎肯定是一个坏主意™。飞行异常导致在堆栈展开时调用析构函数,如果你有抛出异常的析构函数,你最终会发现自己试图同时抛出几个异常。哪个爆炸了。

如果HCAccessor的实例不管理任何资源,则它与清理无关,而析构函数是nop。我不明白这是一个抛出异常的理由 - 只需留下它。

答案 1 :(得分:2)

12.4.3 C ++ 11标准说

  

隐含地认为没有异常规范的析构函数声明   相同的 exception-specification 作为隐式声明。

15.4.14

  

如果 f 是继承构造函数或隐式声明的默认构造函数,则复制   构造函数,移动构造函数,析构函数,复制赋值运算符或移动赋值运算符,其隐式   当且仅当 exception-specification 允许T时,异常规范指定 type-id T   由 f 的隐式定义直接调用的函数; f 允许所有例外(如果有任何直接功能)   invokes允许所有异常, f 具有异常 - 规范 noexcept(true)如果每个函数直接   invokes不允许例外。

由于HCAccessor的隐式析构函数是微不足道的,因此noexcept(true)因为它没有调用任何函数~HCAcessor,所以在没有异常规范的情况下,它将是也声明了noexcept