C++ 你(真的)编写异常安全代码吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1853243/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:24:00  来源:igfitidea点击:

Do you (really) write exception safe code?

c++exception-handling

提问by Frunsi

Exception handling (EH) seems to be the current standard, and by searching the web, I can not find any novel ideas or methods that try to improve or replace it (well, some variations exist, but nothing novel).

异常处理 (EH) 似乎是当前的标准,通过搜索网络,我找不到任何试图改进或替换它的新想法或方法(嗯,存在一些变化,但没有什么新奇)。

Though most people seem to ignore it or just accept it, EH hassome huge drawbacks: exceptions are invisible to the code and it creates many, many possible exit points. Joel on software wrote an article about it. The comparison to gotofits perfect, it made me think again about EH.

尽管大多数人似乎忽略它或只是接受它,但 EH一些巨大的缺点:异常对代码是不可见的,并且它会创建很多很多可能的退出点。乔尔在软件上写了一篇关于它文章goto与完美的对比,让我重新思考了 EH。

I try to avoid EH and just use return values, callbacks or whatever fits the purpose. But when you have to write reliable code, you just can't ignore EH these days: It starts with the new, which may throw an exception, instead of just returning 0 (like in the old days). This makes about any line of C++ code vulnerableto an exception. And then more places in the C++ foundational code throw exceptions... std lib does it, and so on.

我尽量避免 EH,只使用返回值、回调或任何适合目的的东西。但是,当您必须编写可靠的代码时,这些天您不能忽略 EH:它以 开头new,这可能会引发异常,而不是仅返回 0(就像过去一样)。这使得任何 C++ 代码行都容易出现异常。然后 C++ 基础代码中的更多地方抛出异常...... std lib 做到了,依此类推。

This feels like walking on shaky grounds.. So, now we are forced to take care about exceptions!

这感觉就像在摇摇欲坠的地面上行走..所以,现在我们被迫关注异常!

But its hard, its really hard. You have to learn to write exception safe code, and even if you have some experience with it, it will still be required to double check any single line of code to be safe! Or you start to put try/catch blocks everywhere, which clutters the code until it reaches a state of unreadability.

但是很难,真的很难。您必须学习编写异常安全的代码,即使您有一定的经验,仍然需要仔细检查任何一行代码以确保安全!或者你开始在任何地方放置 try/catch 块,这会使代码变得混乱,直到它达到不可读的状态。

EH replaced the old clean deterministical approach (return values..), which had just a few but understandable and easily solveable drawbacks with an approach that creates many possible exit points in your code, and if you start writing code that catches exceptions (what you are forced to do at some point), then it even creates a multitude of paths through your code (code in the catch blocks, think about a server program where you need logging facilities other than std::cerr ..). EH has advantages, but that's not the point.

EH 取代了旧的干净的确定性方法(返回值..),该方法只有一些但可以理解且易于解决的缺点,这种方法会在您的代码中创建许多可能的退出点,并且如果您开始编写捕获异常的代码(在某些时候被迫这样做),然后它甚至会在您的代码中创建多个路径(catch 块中的代码,考虑一个服务器程序,其中您需要除 std::cerr 之外的日志记录工具......)。EH 有优势,但这不是重点。

My actual questions:

我的实际问题:

  • Do you really write exception safe code?
  • Are you sure your last "production ready" code is exception safe?
  • Can you even be sure, that it is?
  • Do you know and/or actually use alternatives that work?
  • 你真的会写异常安全的代码吗?
  • 您确定您最后的“生产就绪”代码是异常安全的吗?
  • 你甚至可以确定,它是吗?
  • 您知道和/或实际使用有效的替代品吗?

回答by paercebal

Your question makes an assertion, that "Writing exception-safe code is very hard". I will answer your questions first, and then, answer the hidden question behind them.

您的问题断言,“编写异常安全的代码非常困难”。我会先回答你的问题,然后再回答背后隐藏的问题。

Answering questions

回答问题

Do you really write exception safe code?

你真的会写异常安全的代码吗?

Of course, I do.

我当然是了。

This is thereason Java lost a lot of its appeal to me as a C++ programmer (lack of RAII semantics), but I am digressing: This is a C++ question.

这就是Java 作为 C++ 程序员(缺乏 RAII 语义)对我失去很多吸引力原因,但我离题了:这是一个 C++ 问题。

It is, in fact, necessary when you need to work with STL or Boost code. For example, C++ threads (boost::threador std::thread) will throw an exception to exit gracefully.

事实上,当您需要使用 STL 或 Boost 代码时,这是必要的。例如,C++ 线程(boost::threadstd::thread)将抛出异常以正常退出。

Are you sure your last "production ready" code is exception safe?

Can you even be sure, that it is?

您确定您最后的“生产就绪”代码是异常安全的吗?

你甚至可以确定,它是吗?

Writing exception-safe code is like writing bug-free code.

编写异常安全的代码就像编写无错误的代码。

You can't be 100% sure your code is exception safe. But then, you strive for it, using well-known patterns, and avoiding well-known anti-patterns.

您不能 100% 确定您的代码是异常安全的。但是,您会为之努力,使用众所周知的模式,并避免使用众所周知的反模式。

Do you know and/or actually use alternatives that work?

您知道和/或实际使用有效的替代品吗?

There are noviable alternatives in C++ (i.e. you'll need to revert back to C and avoid C++ libraries, as well as external surprises like Windows SEH).

C++中没有可行的替代方案(即您需要恢复到 C 并避免使用 C++ 库,以及像 Windows SEH 这样的外部意外)。

Writing exception safe code

编写异常安全代码

To write exception safe code, you must know firstwhat level of exception safety each instruction you write is.

编写异常安全的代码,你必须知道首先每个你写的指令是什么级别的异常安全的。

For example, a newcan throw an exception, but assigning a built-in (e.g. an int, or a pointer) won't fail. A swap will never fail (don't ever write a throwing swap), a std::list::push_backcan throw...

例如,anew可以抛出异常,但分配一个内置的(例如一个 int 或一个指针)不会失败。交换永远不会失败(永远不要写一个抛出交换),一个std::list::push_back可以抛出......

Exception guarantee

异常保证

The first thing to understand is that you must be able to evaluate the exception guarantee offered by all of your functions:

首先要了解的是,您必须能够评估所有函数提供的异常保证:

  1. none: Your code should never offer that. This code will leak everything, and break down at the very first exception thrown.
  2. basic: This is the guarantee you must at the very least offer, that is, if an exception is thrown, no resources are leaked, and all objects are still whole
  3. strong: The processing will either succeed, or throw an exception, but if it throws, then the data will be in the same state as if the processing had not started at all (this gives a transactional power to C++)
  4. nothrow/nofail: The processing will succeed.
  1. none:您的代码永远不应该提供。此代码将泄漏所有内容,并在抛出的第一个异常处崩溃。
  2. basic:这是您至少必须提供的保证,即如果抛出异常,则不会泄漏任何资源,并且所有对象仍然是完整的
  3. strong:处理要么成功,要么抛出异常,但如果抛出,那么数据将处于与处理根本没有开始相同的状态(这赋予了 C++ 事务处理能力)
  4. nothrow/nofail:处理将成功。

Example of code

代码示例

The following code seems like correct C++, but in truth, offers the "none" guarantee, and thus, it is not correct:

下面的代码似乎是正确的 C++,但实际上,提供了“无”保证,因此,它是不正确的:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   X * x = new X() ;                // 2. basic : can throw with new and X constructor
   t.list.push_back(x) ;            // 3. strong : can throw
   x->doSomethingThatCanThrow() ;   // 4. basic : can throw
}

I write all my code with this kind of analysis in mind.

我在编写所有代码时都考虑到了这种分析。

The lowest guarantee offered is basic, but then, the ordering of each instruction makes the whole function "none", because if 3. throws, x will leak.

提供的最低保证是基本的,但是,每条指令的顺序使整个函数“无”,因为如果 3. throws,x 将泄漏。

The first thing to do would be to make the function "basic", that is putting x in a smart pointer until it is safely owned by the list:

首先要做的是使函数“基本”,即将 x 放在智能指针中,直到它被列表安全地拥有:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   std::auto_ptr<X> x(new X()) ;    // 2.  basic : can throw with new and X constructor
   X * px = x.get() ;               // 2'. nothrow/nofail
   t.list.push_back(px) ;           // 3.  strong : can throw
   x.release() ;                    // 3'. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 4.  basic : can throw
}

Now, our code offers a "basic" guarantee. Nothing will leak, and all objects will be in a correct state. But we could offer more, that is, the strong guarantee. This is where it canbecome costly, and this is why not allC++ code is strong. Let's try it:

现在,我们的代码提供了“基本”保证。什么都不会泄漏,所有对象都将处于正确状态。但我们可以提供更多,也就是强有力的保证。这就是它可能变得昂贵的地方,这就是为什么并非所有C++ 代码都强大的原因。让我们试试看:

void doSomething(T & t)
{
   // we create "x"
   std::auto_ptr<X> x(new X()) ;    // 1. basic : can throw with new and X constructor
   X * px = x.get() ;               // 2. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 3. basic : can throw

   // we copy the original container to avoid changing it
   T t2(t) ;                        // 4. strong : can throw with T copy-constructor

   // we put "x" in the copied container
   t2.list.push_back(px) ;          // 5. strong : can throw
   x.release() ;                    // 6. nothrow/nofail
   if(std::numeric_limits<int>::max() > t2.integer)  // 7.   nothrow/nofail
      t2.integer += 1 ;                              // 7'.  nothrow/nofail

   // we swap both containers
   t.swap(t2) ;                     // 8. nothrow/nofail
}

We re-ordered the operations, first creating and setting Xto its right value. If any operation fails, then tis not modified, so, operation 1 to 3 can be considered "strong": If something throws, tis not modified, and Xwill not leak because it's owned by the smart pointer.

我们对操作重新排序,首先创建并设置X为正确的值。如果任何操作失败,则t不会被修改,因此,操作 1 到 3 可以被认为是“强”的:如果有东西抛出,t则不会被修改,并且X不会泄漏,因为它属于智能指针。

Then, we create a copy t2of t, and work on this copy from operation 4 to 7. If something throws, t2is modified, but then, tis still the original. We still offer the strong guarantee.

然后,我们创建一个副本t2t,并从操作4这个副本的工作7.如果抛出的东西,t2被修改,但之后,t仍然是原来的。我们仍然提供强有力的保证。

Then, we swap tand t2. Swap operations should be nothrow in C++, so let's hope the swap you wrote for Tis nothrow (if it isn't, rewrite it so it is nothrow).

然后,我们交换tt2。交换操作在 C++ 中应该是 nothrow,所以让我们希望你写的交换T是 nothrow(如果不是,重写它以便它不会被抛出)。

So, if we reach the end of the function, everything succeeded (No need of a return type) and thas its excepted value. If it fails, then thas still its original value.

所以,如果我们到达函数的末尾,一切都成功了(不需要返回类型)并且t有它的异常值。如果失败,则t仍然具有其原始值。

Now, offering the strong guarantee could be quite costly, so don't strive to offer the strong guarantee to all your code, but if you can do it without a cost (and C++ inlining and other optimization could make all the code above costless), then do it. The function user will thank you for it.

现在,提供强有力的保证可能会非常昂贵,所以不要努力为所有代码提供强有力的保证,但如果你可以不花钱(而且 C++ 内联和其他优化可以使上述所有代码无成本) ,然后去做。功能用户会为此感谢您。

Conclusion

结论

It takes some habit to write exception-safe code. You'll need to evaluate the guarantee offered by each instruction you'll use, and then, you'll need to evaluate the guarantee offered by a list of instructions.

编写异常安全代码需要一些习惯。您需要评估您将使用的每条指令提供的保证,然后您需要评估指令列表提供的保证。

Of course, the C++ compiler won't back up the guarantee (in my code, I offer the guarantee as a @warning doxygen tag), which is kinda sad, but it should not stop you from trying to write exception-safe code.

当然,C++ 编译器不会支持保证(在我的代码中,我将保证作为 @warning doxygen 标签提供),这有点令人难过,但它不应该阻止您尝试编写异常安全的代码。

Normal failure vs. bug

正常失败与错误

How can a programmer guarantee that a no-fail function will always succeed? After all, the function could have a bug.

程序员如何保证无失败函数总是成功?毕竟,该功能可能有错误。

This is true. The exception guarantees are supposed to be offered by bug-free code. But then, in any language, calling a function supposes the function is bug-free. No sane code protects itself against the possibility of it having a bug. Write code the best you can, and then, offer the guarantee with the supposition it is bug-free. And if there is a bug, correct it.

这是真的。异常保证应该由无错误代码提供。但是,在任何语言中,调用函数都假定该函数没有错误。没有任何健全的代码可以保护自己免受出现错误的可能性。尽你所能编写代码,然后提供保证,假设它没有错误。如果有错误,请纠正它。

Exceptions are for exceptional processing failure, not for code bugs.

异常是针对异常处理失败的,而不是针对代码错误的。

Last words

最后的话

Now, the question is "Is this worth it ?".

现在,问题是“这值得吗?”。

Of course, it is. Having a "nothrow/no-fail" function knowing that the function won't fail is a great boon. The same can be said for a "strong" function, which enables you to write code with transactional semantics, like databases, with commit/rollback features, the commit being the normal execution of the code, throwing exceptions being the rollback.

当然是这样。拥有“nothrow/no-fail”功能,知道该功能不会失败,这是一个巨大的福音。对于“强”函数也可以这样说,它使您能够编写具有事务语义的代码,如数据库,具有提交/回滚功能,提交是代码的正常执行,抛出异常是回滚。

Then, the "basic" is the very least guarantee you should offer. C++ is a very strong language there, with its scopes, enabling you to avoid any resource leaks (something a garbage collector would find it difficult to offer for the database, connection or file handles).

然后,“基本”是您应该提供的最起码的保证。C++ 是一种非常强大的语言,它的作用域使您能够避免任何资源泄漏(垃圾收集器很难为数据库、连接或文件句柄提供某些东西)。

So, as far as I see it, it isworth it.

所以,在我看来,这值得的。

Edit 2010-01-29: About non-throwing swap

编辑 2010-01-29:关于非投掷交换

nobar made a comment that I believe, is quite relevant, because it is part of "how do you write exception safe code":

nobar 发表了我认为非常相关的评论,因为它是“如何编写异常安全代码”的一部分:

  • [me] A swap will never fail (don't even write a throwing swap)
  • [nobar] This is a good recommendation for custom-written swap()functions. It should be noted, however, that std::swap()can fail based on the operations that it uses internally
  • [me] 交换永远不会失败(甚至不要写一个抛出交换)
  • [nobar] 对于自定义编写的swap()函数,这是一个很好的建议。但是,应该注意的是,std::swap()根据它内部使用的操作,它可能会失败

the default std::swapwill make copies and assignments, which, for some objects, can throw. Thus, the default swap could throw, either used for your classes or even for STL classes. As far as the C++ standard is concerned, the swap operation for vector, deque, and listwon't throw, whereas it could for mapif the comparison functor can throw on copy construction (See The C++ Programming Language, Special Edition, appendix E, E.4.3.Swap).

默认std::swap将进行复制和分配,对于某些对象,可能会抛出。因此,默认交换可能会抛出,用于您的类甚至 STL 类。至于C ++标准而言,对于交换操作vectordequelist不会抛出,而它可以用于map如果比较仿函数可以拷贝构造(见抛出C ++编程语言,特别版,附录E,E.4.3 .交换)。

Looking at Visual C++ 2008 implementation of the vector's swap, the vector's swap won't throw if the two vectors have the same allocator (i.e., the normal case), but will make copies if they have different allocators. And thus, I assume it could throw in this last case.

查看向量交换的 Visual C++ 2008 实现,如果两个向量具有相同的分配器(即,正常情况),则向量的交换不会抛出,但如果它们具有不同的分配器,则会生成副本。因此,我认为它可能会抛出最后一种情况。

So, the original text still holds: Don't ever write a throwing swap, but nobar's comment must be remembered: Be sure the objects you're swapping have a non-throwing swap.

所以,原文仍然成立:永远不要写一个抛出交换,但必须记住诺巴的评论:确保你正在交换的对象有一个非抛出交换。

Edit 2011-11-06: Interesting article

编辑 2011-11-06:有趣的文章

Dave Abrahams, who gave us the basic/strong/nothrow guarantees, described in an article his experience about making the STL exception safe:

Dave Abrahams为我们提供了basic/strong/nothrow 保证,在一篇文章中描述了他关于使 STL 异常安全的经验:

http://www.boost.org/community/exception_safety.html

http://www.boost.org/community/exception_safety.html

Look at the 7th point (Automated testing for exception-safety), where he relies on automated unit testing to make sure every case is tested. I guess this part is an excellent answer to the question author's "Can you even be sure, that it is?".

看看第 7 点(异常安全的自动化测试),他依靠自动化单元测试来确保每个案例都经过测试。我想这部分是对问题作者“你能确定吗?”的一个很好的回答。

Edit 2013-05-31: Comment from dionadar

编辑 2013-05-31:来自dionadar 的评论

t.integer += 1;is without the guarantee that overflow will not happen NOT exception safe, and in fact may technically invoke UB! (Signed overflow is UB: C++11 5/4 "If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.") Note that unsigned integer do not overflow, but do their computations in an equivalence class modulo 2^#bits.

t.integer += 1;不保证不会发生溢出不是异常安全的,实际上可能在技术上调用 UB!(有符号溢出是 UB: C++11 5/4 “如果在表达式的计算过程中,结果没有在数学上定义或不在其类型的可表示值范围内,则行为未定义。”)请注意,无符号整数不会溢出,而是在等价类模 2^#bits 中进行计算。

Dionadar is referring to the following line, which indeed has undefined behaviour.

Dionadar 指的是以下行,它确实具有未定义的行为。

   t.integer += 1 ;                 // 1. nothrow/nofail

The solution here is to verify if the integer is already at its max value (using std::numeric_limits<T>::max()) before doing the addition.

这里的解决方案是std::numeric_limits<T>::max()在进行加法之前验证整数是否已经达到最大值(使用)。

My error would go in the "Normal failure vs. bug" section, that is, a bug. It doesn't invalidate the reasoning, and it does not mean the exception-safe code is useless because impossible to attain. You can't protect yourself against the computer switching off, or compiler bugs, or even your bugs, or other errors. You can't attain perfection, but you can try to get as near as possible.

我的错误会出现在“正常失败与错误”部分中,即错误。它不会使推理无效,也不意味着异常安全代码是无用的,因为不可能实现。您无法保护自己免受计算机关闭、编译器错误、甚至您的错误或其他错误的影响。你无法达到完美,但你可以尝试尽可能接近。

I corrected the code with Dionadar's comment in mind.

我考虑到 Dionadar 的评论更正了代码。

回答by Joh

Writing exception-safe code in C++ is not so much about using lots of try { } catch { } blocks. It's about documenting what kind of guarantees your code provides.

用 C++ 编写异常安全的代码并不是要使用大量的 try { } catch { } 块。它是关于记录您的代码提供什么样的保证。

I recommend reading Herb Sutter's Guru Of The Weekseries, in particular installments 59, 60 and 61.

我建议阅读 Herb Sutter 的《本周大师》系列,尤其是第 59、60 和 61 期。

To summarize, there are three levels of exception safety you can provide:

总而言之,您可以提供三个级别的异常安全:

  • Basic: When your code throws an exception, your code does not leak resources, and objects remain destructible.
  • Strong: When your code throws an exception, it leaves the state of the application unchanged.
  • No throw: Your code never throws exceptions.
  • 基本:当您的代码抛出异常时,您的代码不会泄漏资源,并且对象保持可破坏性。
  • 强:当您的代码抛出异常时,它会使应用程序的状态保持不变。
  • 不抛出:您的代码从不抛出异常。

Personally, I discovered these articles quite late, so much of my C++ code is definitely not exception-safe.

就我个人而言,我发现这些文章很晚,所以我的大部分 C++ 代码绝对不是异常安全的。

回答by bmargulies

Some of us have been using exception for over 20 years. PL/I has them, for example. The premise that they are a new and dangerous technology seems questionable to me.

我们中的一些人已经使用异常超过 20 年了。例如,PL/I 有它们。它们是一种新的危险技术的前提在我看来是有问题的。

回答by D.Shawley

First of all (as Neil stated), SEH is Microsoft's Structured Exception Handling. It is similar to but not identical to exception processing in C++. In fact, you have to enable C++ Exception Handlingif you want it in Visual Studio - the default behavior does not guarantee that local objects are destroyed in all cases! In either case, Exception Handling is not really harderit is just different.

首先(正如尼尔所说),SEH 是微软的结构化异常处理。它与 C++ 中的异常处理类似但不完全相同。事实上,如果你想在 Visual Studio 中启用 C++ 异常处理,你必须启用它 - 默认行为并不能保证在所有情况下都销毁本地对象!在任何一种情况下,异常处理都不是很难,只是不同

Now for your actual questions.

现在回答你的实际问题。

Do you really write exception safe code?

你真的会写异常安全的代码吗?

Yes. I strive for exception safe code in all cases. I evangelize using RAII techniques for scoped access to resources (e.g., boost::shared_ptrfor memory, boost::lock_guardfor locking). In general, consistent usage of RAIIand scope guardingtechniques will make exception safe code much easier to write. The trick is to learn what exists and how to apply it.

是的。我在所有情况下都争取异常安全的代码。我宣传使用 RAII 技术对资源进行范围访问(例如,boost::shared_ptr内存、boost::lock_guard锁定)。一般来说,一致使用RAII范围保护技术将使异常安全代码更容易编写。诀窍是了解存在的内容以及如何应用它。

Are you sure your last "production ready" code is exception safe?

您确定您最后的“生产就绪”代码是异常安全的吗?

No. It is as safe as it is. I can say that I haven't seen a process fault due to an exception in several years of 24/7 activity. I don't expect perfect code, just well-written code. In addition to providing exception safety, the techniques above guarantee correctness in a way that is near impossible to achieve with try/catchblocks. If you are catching everything in your top control scope (thread, process, etc.), then you can be sure that you will continue to run in the face of exceptions (most of the time). The same techniques will also help you continue to run correctlyin the face of exceptions without try/catchblocks everywhere.

不,它和它一样安全。我可以说,在几年 24/7 的活动中,我没有看到由于异常而导致的流程错误。我不期望完美的代码,只期望编写好的代码。除了提供异常安全之外,上述技术以一种几乎不可能用try/catch块实现的方式来保证正确性。如果您正在捕获顶级控制范围内的所有内容(线程、进程等),那么您可以确定您将在遇到异常时继续运行(大部分时间)。相同的技术也将帮助您在遇到异常时继续正确运行,而没有try/catch块无处不在

Can you even be sure that it is?

你能确定它是吗?

Yes. You can be sure by a thorough code audit but no one really does that do they? Regular code reviews and careful developers go a long way to getting there though.

是的。您可以通过彻底的代码审计来确定,但没有人真的这样做吗?不过,定期的代码和细心的开发人员对实现这一目标大有帮助。

Do you know and/or actually use alternatives that work?

您知道和/或实际使用有效的替代品吗?

I have tried a few variations over the years such as encoding states in the upper bits (ala HRESULTs) or that horrible setjmp() ... longjmp()hack. Both of these break down in practice though in completely different ways.

多年来,我尝试了一些变体,例如高位编码状态(ala HRESULTs)或那个可怕的setjmp() ... longjmp()黑客。这两者在实践中都以完全不同的方式分解。



In the end, if you get into the habit of applying a few techniques and carefully thinking about where you can actually do something in response to an exception, you will end up with very readable code that is exception safe. You can sum this up by following these rules:

最后,如果你养成应用一些技术的习惯,并仔细考虑你可以在哪些地方实际做一些事情来响应异常,你最终会得到非常可读的异常安全代码。您可以按照以下规则进行总结:

  • You only want to see try/catchwhen you can do something about a specific exception
  • You almost never want to see a raw newor deletein code
  • Eschew std::sprintf, snprintf, and arrays in general - use std::ostringstreamfor formatting and replace arrays with std::vectorand std::string
  • When in doubt, look for functionality in Boost or STL before rolling your own
  • 您只想查看try/catch何时可以对特定异常执行某些操作
  • 你几乎不想看到原始newdelete代码
  • 避开std::sprintfsnprintf和数组一般-使用std::ostringstream用于格式化和替换阵列std::vectorstd::string
  • 如有疑问,请在推出自己的功能之前在 Boost 或 STL 中查找功能

I can only recommend that you learn how to use exceptions properly and forget about result codes if you plan on writing in C++. If you want to avoid exceptions, you might want to consider writing in another language that either does not have themor makes them safe. If you want to really learn how to fully utilize C++, read a few books from Herb Sutter, Nicolai Josuttis, and Scott Meyers.

如果您打算用 C++ 编写,我只能建议您学习如何正确使用异常并忘记结果代码。如果您想避免异常,您可能需要考虑使用另一种没有异常或使其安全的语言编写。如果您想真正了解如何充分利用 C++,请阅读Herb SutterNicolai JosuttisScott Meyers的几本书。

回答by AnT

It is not possible to write exception-safe code under the assumption that "any line can throw". The design of exception-safe code relies critically on certain contracts/guarantees that you are supposed to expect, observe, follow and implement in your code. It is absolutely necessary to have code that is guaranteed to neverthrow. There are other kinds of exception guarantees out there.

在“任何行都可以抛出”的假设下编写异常安全代码是不可能的。异常安全代码的设计严重依赖于您应该在代码中期望、观察、遵循和实现的某些契约/保证。保证永远不会抛出的代码是绝对必要的。还有其他类型的异常保证。

In other words, creating exception-safe code is to a large degree a matter of program designnot just a matter of plain coding.

换句话说,创建异常安全代码在很大程度上是程序设计的问题,而不仅仅是普通编码的问题

回答by AnT

  • Do you really write exception safe code?
  • 你真的会写异常安全的代码吗?

Well, I certainly intend to.

嗯,我当然打算。

  • Are you sure your last "production ready" code is exception safe?
  • 您确定您最后的“生产就绪”代码是异常安全的吗?

I'm sure that my 24/7 servers built using exceptions run 24/7 and don't leak memory.

我确信我使用异常构建的 24/7 服务器 24/7 运行并且不会泄漏内存。

  • Can you even be sure, that it is?
  • 你甚至可以确定,它是吗?

It's very difficult to be sure that any code is correct. Typically, one can only go by results

很难确保任何代码都是正确的。通常,人们只能通过结果

  • Do you know and/or actually use alternatives that work?
  • 您知道和/或实际使用有效的替代品吗?

No. Using exceptions is cleaner and easier than any of the alternatives I've used over the last 30 years in programming.

不。使用异常比我在过去 30 年的编程中使用的任何替代方法都更清晰、更容易。

回答by Mark Bessey

Leaving aside the confusion between SEH and C++ exceptions, you need to be aware that exceptions can be thrown at any time, and write your code with that in mind. The need for exception-safety is largely what drives the use of RAII, smart pointers, and other modern C++ techniques.

撇开 SEH 和 C++ 异常之间的混淆不谈,您需要知道异常可以随时抛出,并在编写代码时考虑到这一点。对异常安全的需求在很大程度上推动了 RAII、智能指针和其他现代 C++ 技术的使用。

If you follow the well-established patterns, writing exception-safe code is not particularly hard, and in fact it's easier than writing code that handles error returns properly in all cases.

如果遵循完善的模式,编写异常安全的代码并不是特别困难,事实上,它比编写在所有情况下都能正确处理错误返回的代码更容易。

回答by Mr. Boy

EH is good, generally. But C++'s implementation is not very friendly as it's really hard to tell how good your exception catching coverage is. Java for instance makes this easy, the compiler will tend to fail if you don't handle possible exceptions .

EH 很好,一般来说。但是 C++ 的实现并不是很友好,因为很难判断您的异常捕获覆盖范围有多好。例如,Java 使这变得容易,如果您不处理可能的异常,编译器往往会失败。

回答by Charles Eli Cheese

  • Do you really write exception safe code? [There's no such thing. Exceptions are a paper shield to errors unless you have a managed environment. This applies to first three questions.]

  • Do you know and/or actually use alternatives that work? [Alternative to what? The problem here is people don't separate actual errors from normal program operation. If it's normal program operation (ie a file not found), it's not really error handling. If it's an actual error, there is no way to 'handle' it or it's not an actual error. Your goal here is to find out what went wrong and either stop the spreadsheet and log an error, restart the driver to your toaster, or just pray that the jetfighter can continue flying even when it's software is buggy and hope for the best.]

  • 你真的会写异常安全的代码吗?【没有这种事。除非您有托管环境,否则异常是防止错误的纸面盾牌。这适用于前三个问题。]

  • 您知道和/或实际使用有效的替代品吗?【替代什么?这里的问题是人们没有将实际错误与正常程序操作分开。如果是正常的程序操作(即未找到文件),则不是真正的错误处理。如果这是一个实际错误,则无法“处理”它,或者它不是实际错误。您在这里的目标是找出哪里出了问题,或者停止电子表格并记录错误,重新启动烤面包机的驱动程序,或者只是祈祷喷气式战斗机可以继续飞行,即使它的软件有问题,并希望一切顺利。]

回答by Crowe T. Robot

I really like working with Eclipse and Java though (new to Java), because it throws errors in the editor if you are missing an EH handler. That makes things a LOT harder to forget to handle an exception...

我真的很喜欢使用 Eclipse 和 Java(Java 新手),因为如果您缺少 EH 处理程序,它会在编辑器中引发错误。这使得忘记处理异常变得更加困难......

Plus, with the IDE tools, it adds the try / catch block or another catch block automatically.

另外,使用 IDE 工具,它会自动添加 try / catch 块或另一个 catch 块。