Google的Go语言作为设计选择没有例外,Linux的Linus称为异常。 为什么?
Google's Go language has no exceptions as a design choice, and Linus of Linux fame has called exceptions crap. Why?
最满意答案
异常使得编写代码非常容易,抛出异常将会破坏不变量并使对象处于不一致的状态。 他们基本上强迫你记住,你所做的每一个声明都可能会抛出,并正确地处理。 这样做可能是棘手和反直觉的。
考虑像这样的一个简单的例子:
class Frobber { private: int m_NumberOfFrobs; FrobManager m_FrobManager; public: void Frob() { m_NumberOfFrobs++; m_FrobManager.HandleFrob(new FrobObject()); } };假设FrobManager将delete FrobObject ,这看起来FrobObject ,对吧? 或者也许不是...想象一下,如果FrobManager::HandleFrob()或operator new抛出异常。 在此示例中, m_NumberOfFrobs的增量不会回滚。 因此,任何使用这个Frobber实例的Frobber都会有可能被破坏的对象。
这个例子可能看起来很愚蠢(好的,我不得不伸展自己来构造一个:-)),但是外部的是,如果一个程序员不是一直在思考异常,并且确保每一个状态的排列被滚动回来,无论如何,你会遇到这样的麻烦。
作为一个例子,您可以像想到互斥体那样想到它。 在关键部分中,您依靠几个语句来确保数据结构不会损坏,而其他线程无法看到您的中间值。 如果这些言论中的任何一个只是随机地运行,那么你最终会陷入痛苦的世界。 现在拿走锁和并发,并考虑每一种方法。 将每个方法视为对象状态的排列事务,如果你愿意的话。 在方法调用开始时,对象应该是清洁状态,最后还应该有一个干净的状态。 在之间,变量foo可能与bar不一致,但是您的代码最终会纠正。 什么例外意味着你的任何一个陈述都可以在任何时候打断你。 在这种情况下,每个单独的方法都可以让你正确地回滚,或者命令你的操作,所以抛出不影响对象状态。 如果你错了(这很容易犯这种错误),那么调用者就会看到你的中间值。
诸如RAII的方法,C ++程序员喜欢提到的这个问题的最终解决方案,很大程度上可以防止这种情况。 但他们不是一个银弹。 它将确保您抛出资源,但不会让您无需考虑对象状态的损坏和看到中间值的调用者。 所以,对很多人来说,用编程风格来说比较容易, 没有例外 。 如果限制您编写的代码类型,那么很难介绍这些错误。 如果你不这样做,那就很容易犯错误。
关于C ++中的异常安全编码的整本书已经写完了。 很多专家错了。 如果它真的那么复杂,有这么多的细微差别,也许这是一个很好的迹象,你需要忽略这个功能。 :-)
Exceptions make it really easy to write code where an exception being thrown will break invariants and leave objects in an inconsistent state. They essentially force you to remember that most every statement you make can potentially throw, and handle that correctly. Doing so can be tricky and counter-intuitive.
Consider something like this as a simple example:
class Frobber { int m_NumberOfFrobs; FrobManager m_FrobManager; public: void Frob() { m_NumberOfFrobs++; m_FrobManager.HandleFrob(new FrobObject()); } };Assuming the FrobManager will delete the FrobObject, this looks OK, right? Or maybe not... Imagine then if either FrobManager::HandleFrob() or operator new throws an exception. In this example, the increment of m_NumberOfFrobs does not get rolled back. Thus, anyone using this instance of Frobber is going to have a possibly corrupted object.
This example may seem stupid (ok, I had to stretch myself a bit to construct one :-)), but, the takeaway is that if a programmer isn't constantly thinking of exceptions, and making sure that every permutation of state gets rolled back whenever there are throws, you get into trouble this way.
As an example, you can think of it like you think of mutexes. Inside a critical section, you rely on several statements to make sure that data structures are not corrupted and that other threads can't see your intermediate values. If any one of those statements just randomly doesn't run, you end up in a world of pain. Now take away locks and concurrency, and think about each method like that. Think of each method as a transaction of permutations on object state, if you will. At the start of your method call, the object should be clean state, and at the end there should also be a clean state. In between, variable foo may be inconsistent with bar, but your code will eventually rectify that. What exceptions mean is that any one of your statements can interrupt you at any time. The onus is on you in each individual method to get it right and roll back when that happens, or order your operations so throws don't effect object state. If you get it wrong (and it's easy to make this kind of mistake), then the caller ends up seeing your intermediate values.
Methods like RAII, which C++ programmers love to mention as the ultimate solution to this problem, go a long way to protect against this. But they aren't a silver bullet. It will make sure you release resources on a throw, but doesn't free you from having to think about corruption of object state and callers seeing intermediate values. So, for a lot of people, it's easier to say, by fiat of coding style, no exceptions. If you restrict the kind of code you write, it's harder to introduce these bugs. If you don't, it's fairly easy to make a mistake.
Entire books have been written about exception safe coding in C++. Lots of experts have gotten it wrong. If it's really that complex and has so many nuances, maybe that's a good sign that you need to ignore that feature. :-)
为什么异常处理不好?(Why is exception handling bad?)Google的Go语言作为设计选择没有例外,Linux的Linus称为异常。 为什么?
Google's Go language has no exceptions as a design choice, and Linus of Linux fame has called exceptions crap. Why?
最满意答案
异常使得编写代码非常容易,抛出异常将会破坏不变量并使对象处于不一致的状态。 他们基本上强迫你记住,你所做的每一个声明都可能会抛出,并正确地处理。 这样做可能是棘手和反直觉的。
考虑像这样的一个简单的例子:
class Frobber { private: int m_NumberOfFrobs; FrobManager m_FrobManager; public: void Frob() { m_NumberOfFrobs++; m_FrobManager.HandleFrob(new FrobObject()); } };假设FrobManager将delete FrobObject ,这看起来FrobObject ,对吧? 或者也许不是...想象一下,如果FrobManager::HandleFrob()或operator new抛出异常。 在此示例中, m_NumberOfFrobs的增量不会回滚。 因此,任何使用这个Frobber实例的Frobber都会有可能被破坏的对象。
这个例子可能看起来很愚蠢(好的,我不得不伸展自己来构造一个:-)),但是外部的是,如果一个程序员不是一直在思考异常,并且确保每一个状态的排列被滚动回来,无论如何,你会遇到这样的麻烦。
作为一个例子,您可以像想到互斥体那样想到它。 在关键部分中,您依靠几个语句来确保数据结构不会损坏,而其他线程无法看到您的中间值。 如果这些言论中的任何一个只是随机地运行,那么你最终会陷入痛苦的世界。 现在拿走锁和并发,并考虑每一种方法。 将每个方法视为对象状态的排列事务,如果你愿意的话。 在方法调用开始时,对象应该是清洁状态,最后还应该有一个干净的状态。 在之间,变量foo可能与bar不一致,但是您的代码最终会纠正。 什么例外意味着你的任何一个陈述都可以在任何时候打断你。 在这种情况下,每个单独的方法都可以让你正确地回滚,或者命令你的操作,所以抛出不影响对象状态。 如果你错了(这很容易犯这种错误),那么调用者就会看到你的中间值。
诸如RAII的方法,C ++程序员喜欢提到的这个问题的最终解决方案,很大程度上可以防止这种情况。 但他们不是一个银弹。 它将确保您抛出资源,但不会让您无需考虑对象状态的损坏和看到中间值的调用者。 所以,对很多人来说,用编程风格来说比较容易, 没有例外 。 如果限制您编写的代码类型,那么很难介绍这些错误。 如果你不这样做,那就很容易犯错误。
关于C ++中的异常安全编码的整本书已经写完了。 很多专家错了。 如果它真的那么复杂,有这么多的细微差别,也许这是一个很好的迹象,你需要忽略这个功能。 :-)
Exceptions make it really easy to write code where an exception being thrown will break invariants and leave objects in an inconsistent state. They essentially force you to remember that most every statement you make can potentially throw, and handle that correctly. Doing so can be tricky and counter-intuitive.
Consider something like this as a simple example:
class Frobber { int m_NumberOfFrobs; FrobManager m_FrobManager; public: void Frob() { m_NumberOfFrobs++; m_FrobManager.HandleFrob(new FrobObject()); } };Assuming the FrobManager will delete the FrobObject, this looks OK, right? Or maybe not... Imagine then if either FrobManager::HandleFrob() or operator new throws an exception. In this example, the increment of m_NumberOfFrobs does not get rolled back. Thus, anyone using this instance of Frobber is going to have a possibly corrupted object.
This example may seem stupid (ok, I had to stretch myself a bit to construct one :-)), but, the takeaway is that if a programmer isn't constantly thinking of exceptions, and making sure that every permutation of state gets rolled back whenever there are throws, you get into trouble this way.
As an example, you can think of it like you think of mutexes. Inside a critical section, you rely on several statements to make sure that data structures are not corrupted and that other threads can't see your intermediate values. If any one of those statements just randomly doesn't run, you end up in a world of pain. Now take away locks and concurrency, and think about each method like that. Think of each method as a transaction of permutations on object state, if you will. At the start of your method call, the object should be clean state, and at the end there should also be a clean state. In between, variable foo may be inconsistent with bar, but your code will eventually rectify that. What exceptions mean is that any one of your statements can interrupt you at any time. The onus is on you in each individual method to get it right and roll back when that happens, or order your operations so throws don't effect object state. If you get it wrong (and it's easy to make this kind of mistake), then the caller ends up seeing your intermediate values.
Methods like RAII, which C++ programmers love to mention as the ultimate solution to this problem, go a long way to protect against this. But they aren't a silver bullet. It will make sure you release resources on a throw, but doesn't free you from having to think about corruption of object state and callers seeing intermediate values. So, for a lot of people, it's easier to say, by fiat of coding style, no exceptions. If you restrict the kind of code you write, it's harder to introduce these bugs. If you don't, it's fairly easy to make a mistake.
Entire books have been written about exception safe coding in C++. Lots of experts have gotten it wrong. If it's really that complex and has so many nuances, maybe that's a good sign that you need to ignore that feature. :-)
发布评论