2015-01-02 105 views
1

我试图创建一个“大多不变”的类,它允许客户端如果需要破坏不变量,但仅当它们在离开范围之前修复它发生这种糟糕的事情。从守卫类析构函数抛出异常导致std :: terminate

以下是涉及的两个类。这与范围守卫相似。更多细节,评论和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被调用。

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

+0

不要[在析构函数中抛出异常](http://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor)。 –

+0

除了在析构函数中抛出异常是不好的,你实际上是在试图捕获异常吗?你如何处理'HCAccessor'类的临时对象?还是你在任何地方通过价值传递它? –

回答

3

C++ 11中的析构函数默认为noexcept。如果你真的想这样做,你有noexcept(false)申报HCAccessor的析构函数:

~HCAccessor() noexcept(false) { 

或老式抛列表:

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

(荣誉给普拉丹为noexcept(false)语法我以前没有见过,这不是我们经常需要的东西,我想)。

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

如果HCAccessor的实例没有管理任何资源,它在清理时没有任何操作,而析构函数是一个nop。我不明白这是如何抛出异常的原因 - 只要保留即可。

+0

我一直在想这个,如果抛出一个无关的异常并且'HasConditions'也是无效的,那么你实际上一次有两个问题,它不仅仅是这个构造的一个神器。有没有办法处理这个问题? – Praxeolitic

+0

不是。你只需要设计你的类,使得无效对象不会发生,或者无效对象的销毁不会导致问题。后者通常不是很难做到。 @Pradhan:唉,那有时候是这样。如果你不介意的话,我会公然为这个邪恶的案件窃取更好的语法。 – Wintermute

2

的C++ 11标准的12.4.3

析构函数的声明不具有一个异常规范被隐式地认为具有 相同异常规范作为隐式宣言。

15.4.14

如果˚F是继承构造函数或隐式声明的默认构造函数,复制 构造,移动构造函数,析构函数,拷贝赋值运算符,或移动赋值运算符,其隐含的 异常规范指定type-id T当且仅当T被例外规范允许时 直接调用的函数f的隐式定义; ˚F允许所有的异常,如果任何功能直接 所调用允许所有的异常,并˚F异常规范noexcept(真)如果每个功能直接 所调用允许没有例外。

由于用于HCAccessor隐式析构函数将是微不足道的并且因此noexcept(true),因为它不调用任何功能,~HCAcessor,在没有异常规范的,将被宣布noexcept为好。

相关问题