C ++异常安全偏执:多少钱太多了?

时间:2012-03-28 14:30:04

标签: c++ exception-safety

强大的异常安全保证表明,如果发生异常,操作不会更改任何程序状态。实现异常安全副本分配的一种优雅方式是copy-and-swap idiom

我的问题是:

  1. 对于改变非原始类型的类的每个变异操作,使用copy-and-swap是否过度?

  2. 对于强烈的异常安全,性能真的是公平的交易吗?

  3. 例如:

    class A
    {   
        public:
            void increment()
            {   
                // Copy
                A tmp(*this);
    
                // Perform throwing operations on the copy
                ++(tmp.x);
                tmp.x.crazyStuff();
    
                // Now that the operation is done sans exceptions, 
                // change program state
                swap(tmp);
            }   
    
            int setSomeProperty(int q)
            {   
                A tmp(*this);
                tmp.y.setProperty("q", q); 
                int rc = tmp.x.otherCrazyStuff();
                swap(tmp);
                return rc;
            }   
    
            //
            // And many others similarly
            //
    
            void swap(const A &a) 
            {   
                // Non-throwing swap
            }   
    
        private:
            SomeClass x;
            OtherClass y;
    };
    

4 个答案:

答案 0 :(得分:3)

您应始终瞄准基本异常保证:确保在发生异常时,所有资源都已正确释放且对象处于有效状态(可以是未定义但有效)。

强烈的异常保证(即“交易”)是您认为有意义时应该实现的:您并不总是需要交易行为。

如果很容易实现交易操作(例如通过复制和交换),那么就这样做。但有时它不是,或者它会产生很大的性能影响,即使对于像赋值算子这样的基本事物也是如此。我记得像boost :: variant这样的东西,我不能总是在复制赋值中提供强有力的保证。

你遇到的一个巨大困难是移动语义。移动时想要交易,否则会丢失移动的对象。但是,您无法始终提供强有力的保证:考虑std::pair<movable_nothrow, copyable>(并查看评论)。这是你必须成为noexcept艺术家的地方,并使用一个令人不舒服的元编程。由于异常安全,C ++很难精确地

答案 1 :(得分:2)

所有工程问题都与平衡有关。

当然,const - ness / immutability和强大的保证增加了对一个人代码的信心(特别是伴随着测试)。它们还有助于减少可能的错误解释空间。

但是,它们可能会对性能产生影响。

就像所有表现问题一样,我会说:个人资料并摆脱热点。复制和交换肯定是不是实现事务语义的唯一方法(它只是最简单的),因此分析会告诉你应该绝对不使用它,你必须找到替代方案。 / p>

答案 2 :(得分:1)

这取决于您的应用程序将在何种环境中运行。如果您只是在自己的计算机上运行它(频谱的一端),则可能不值得对异常安全性过于严格。如果你正在写一个程序,例如对于医疗设备(另一端),您不希望在发生异常时留下无意的副作用。中间的任何内容取决于对错误的容忍程度和可用于开发的资源(时间,金钱等)

答案 3 :(得分:0)

是的,你面临的问题是这个成语很难扩展。其他答案都没有提到它,但Alexandrescu发明的另一个非常有趣的成语是scopeGuards。它有助于提高代码的经济性,并在需要符合强大异常安全保证的函数的可读性方面做出巨大改进。

范围保护的想法是一个堆栈实例,它只允许将回滚功能对象附加到每个资源请求。当范围保护被破坏(通过例外)时,将调用回滚。您需要在正常流中显式调用commit()以避免在作用域出口处进行回滚调用。

使用c ++ 11功能检查与设计安全范围保护相关的this recent question from me