在设计时考虑到异常安全的方法/类中提供强有力的保证

时间:2013-08-02 09:27:58

标签: c++ exception-handling error-handling

我遇到了设计问题。让我们先说这段代码

struct Trial{
    const double a;
    Trial(double a_) : a(a_){}
};

int main(){
    Trial t1(1.);
    Trial t2(2.);
    t1 = t2;
}

无法编译,因为编译器默认不构建Trial::operator=,因为Trial::aconst。这非常明显。

现在重点是,首先是代码

struct Trial{
    const double a;
    Trial(double a_) : a(a_){}
};

struct MyClass : private Trial{
    MyClass(double a_) : Trial(a_), i(0) {};
    void wannaBeStrongGuaranteeMemberFunction(){
        MyClass old(i);
        bool failed = true;
        //... try to perform operations here
        if(failed)
            *this = old;
    }
    unsigned int i;
};

int main(){
    MyClass m1(1.);
    m1.wannaBeStrongGuaranteeMemberFunction();
}

我需要为类的某些方法提供强大的异常安全性,这些方法派生自Trial。这些方法对无穷无尽的一系列成员(示例中为i)执行无休止的一系列操作,这使得“手动”恢复操作变得不切实际。因此,我决定最好执行整个类的副本,如果有任何失败则将其复制回来。

小括号,代码只是一个例子。遗留的现实世界代码中的一切都要复杂得多。 在这个例子中,只复制i就可以了,但实际代码并非如此。 此外,操作具有多个(和复杂的)执行路径,因此将它们“读取”为“事务”将是一种痛苦。 此外,我正在使用范围保护来实现这一点,因此在实际代码中正确地管理异常。

当然整个事情都没有编译,因为行*this = old

您如何解决问题/解决问题?

1 个答案:

答案 0 :(得分:2)

显而易见的答案是修改Trial以便它支持 任务也是如此。除非这一点,如果你唯一的原因 想支持任务是提供强有力的保证, 你可以实现自己的赋值运算符 private,忽略了基类;既然你知道那个 两个基类是相同的,没有必要分配 他们之间。

请注意,强保证通常涉及交换 分配。哪个不会改变问题:你不能交换 两个版本的Trial也是。你很有可能 类似的东西:

class MyClass : private Trial
{
    class Protected
    {
        bool myCommitted;
        MyClass* myOwner;
        MyClass myInstance;
    public:
        MyClass( MyClass& owner )
            : myCommitted( false )
            , myOwner( &owner )
            , myInstance( owner )
        {
        }
        ~MyClass()
        {
            if ( myCommitted ) {
                myOwner->swap( myInstance );
            }
        }
        MyClass& instance() { return myInstance; }
        void commit() { myCommitted = true; }
    };

public:

    void protectedFunc()
    {
        Protected p( *this );
        p.instance().unprotecedVersionOfFunc();
        //  ...
        p.commit();
    }

任何异常都会使对象保持不变。 (你可以, 当然,颠倒逻辑,在this上进行修改, 如果没有提交则交换。)做事的好处 这样你就可以“撤消”内存中的任何变化 分配等。

最后:你真的想在Trial中使用const成员吗? 实现这一目标的通常方法是制作a 非const但是私有,只提供一个getter。这意味着 Trial::a实际上是const,之外是完整的 分配

相关问题