缺点是C++中的异常

kun*_*ami 14 c++

我正在阅读谷歌C++风格指南,并在例外部分感到困惑.根据指南,使用它的一个缺点是:

异常安全需要RAII和不同的编码实践.需要大量的支持机制才能使编写正确的异常安全代码变得容易.此外,为了避免要求读者理解整个调用图,异常安全代码必须将写入持久状态的逻辑隔离到"提交"阶段.这将带来好处和成本(可能是您被迫混淆代码以隔离提交的地方).允许例外将迫使我们总是支付这些费用,即使它们不值得

具体来说,我不明白的陈述是这样的:

(...)异常安全代码必须将写入持久状态的逻辑隔离到"提交"阶段.

还有这个:

(...)也许你被迫混淆代码来隔离提交(...).

我想我不习惯术语"持久状态","提交阶段","混淆代码以隔离提交".关于这些术语的一些小解释,例子或参考可能是很好的,也可能是为什么这是真的.

GMa*_*ckG 36

基本上,现代C++使用范围限制资源管理(SBRM或RAII).也就是说,一个对象清理其析构函数中的资源,该资源可以保证被调用.

除非您的代码不是现代的,否则这一切都很好.例如:

int *i = new int();
do_something(i);
delete i;
Run Code Online (Sandbox Code Playgroud)

如果do_something抛出异常,你就泄露了.正确的解决方案是不要像这样在野外放置资源,即:

std::auto_ptr<int> i(new int());
do_something(i.get());
Run Code Online (Sandbox Code Playgroud)

现在它永远不会泄漏(代码更清洁!).

我认为指南试图说的是我们已经拥有了所有这些旧代码,使用现代风格需要花费太多精力.所以我们不要使用例外.(而不是修复所有代码......我非常不喜欢Google Style Guide.)

  • 我之前没有听说过"SBRM"这个词.它比RAII更具描述性.我希望它能够抓住. (22认同)
  • +1不喜欢谷歌风格指南(它非常适合内部使用,但它不是我想要在新的现代项目上强加的东西).NB.我会给你加+1而其余的也是:-) (8认同)
  • @Chris:如果我们谈论同样的Joel/Raymond文章,他们并没有给我留下深刻印象.引用的Raymond Chen专栏Joel表示没有掌握RAII在异常安全中的使用,并且理解RAII的作用是我在这个问题上值得一读的最低标准. (3认同)
  • @Fred:我也是,这就是我到处使用它的原因。我将对维基百科进行编辑,并就此进行 CW 问答。我正在尝试 [追踪](http://stackoverflow.com/questions/3119197/whats-the-right-approach-for-error-handling-in-c/3119202#3119202) 新术语的来源. (2认同)
  • RAII 是一个糟糕的术语,因为真正重要的是资源放弃即破坏。SBRM 更好,但我担心 RAII 太根深蒂固了。 (2认同)

Jam*_*ran 9

"写入持久状态" 大致意味着" 写入文件 "或" 写入数据库 ".

"进入'提交'阶段." 大致意思是"一次完成所有写作"

"也许你被迫混淆代码以隔离提交"意味着" 这可能会使代码难以阅读 "(轻微滥用"混淆"一词意味着故意使某些东西难以阅读,而在这里它们意味着无意中使它难以阅读,但滥用可能是故意的,因为戏剧性的影响)

详细阐述:"写入持久状态"更接近意味着"向某些永久性媒体写出有关此对象的所有细节,以便重新创建它".如果写入被异常中断,则那些"写出的细节"(即"持久状态")可能包含新状态的一半和旧状态的一半,从而在重新创建时导致无效对象.因此,写国家必须作为一个不间断的行为.

  • 实际上,通过"持久状态",它们几乎肯定意味着外人可以观察到的状态.它比堆上的变量更通用.例如,请参阅有关软件事务内存的工作. (3认同)

bsh*_*lds 9

关于持久状态的说法是这样的:即使你正在使用RAII并且你的对象被正确破坏,允许你清理,如果try块中的代码以某种方式修改了系统的状态,你很可能需要找出如何回滚这些更改,因为操作未成功完成.他们在这里使用术语"提交",因为它与事务有关,这一概念是,当您执行操作时,系统的状态应该好像是100%成功完成或根本没有发生.

以下是即使使用RAII,这也会变得混乱:

struct MyClass
{
    MyClass(Foo* foo) 
    { 
        m_bar = new Bar;
        foo->changeSomeState(); 
    }

    ~MyClass()
    {
        delete m_bar;
    }

    Bar* m_bar;
};
Run Code Online (Sandbox Code Playgroud)

现在,如果你有这个代码:

try
{
    MyClass myClass(foo);
    Baz baz;
    baz.doSomething(); // Throws an exception
}
catch(...)
{
    // MyClass doesn't leak memory, but should it try to undo
    // the change it made to foo?
}
Run Code Online (Sandbox Code Playgroud)

因此,为了正确处理这种情况,您必须添加更多代码以将其视为事务,并在抛出异常时回滚对try块中的持久状态所做的任何更改.他们只是说强制事务语义会使代码混乱(混淆).

我不同意禁止例外,顺便说一下,只是试图表明他们所指的问题.

  • @Brendan Long显然可以无异常地进行正确的错误处理.然而,我更喜欢他们,正如我所说,我不同意他们的推理.但我认为这无疑说明了他们正在制造的论点,这就是问题的关键所在. (2认同)