我应该在C ++中使用exception说明符吗?
在C ++中,您可以指定一个函数可以或不可以通过使用exception说明符来引发exception。 例如:
void foo() throw(); // guaranteed not to throw an exception void bar() throw(int); // may throw an exception of type int void baz() throw(...); // may throw an exception of some unspecified type
我怀疑实际使用它们是因为以下原因:
- 编译器没有以任何严格的方式强制执行exception说明符,所以好处不大。 理想情况下,你想得到一个编译错误。
- 如果一个函数违反了一个exception说明符,我认为标准的行为是终止程序。
- 在VS.Net中,它将throw(X)视为throw(…),所以遵守标准并不强烈。
你认为应该使用exception说明符吗?
请回答“是”或“否”,并提供一些理由来certificate您的答案。
没有。
这里有几个例子:
-
模板代码是不可能写exception规范的,
template<class T> void f( T k ) { T x( k ); xx(); }
副本可能会抛出,parameter passing可能会抛出,而
x()
可能会抛出一些未知的exception。 -
exception规范往往禁止扩展性。
virtual void open() throw( FileNotFound );
可能演变成
virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
你真的可以这样写
throw( ... )
第一个是不可扩展的,第二个是过度的,第三个是你写的虚拟函数的意思。
-
遗留代码
当你编写依赖于另外一个库的代码时,你真的不知道当可怕的错误发生时它会做什么。
int lib_f(); void g() throw( k_too_small_exception ) { int k = lib_f(); if( k < 0 ) throw k_too_small_exception(); }
g
将在lib_f()
抛出时终止。 这(大多数情况下)不是你真正想要的。 不应该调用std::terminate()
。 让应用程序崩溃是一个比较好的办法,那就是用一个未处理的exception(从中可以检索一个堆栈跟踪),而不是无声无息地死亡。 -
编写返回常见错误并在特殊情况下抛出的代码。
Error e = open( "bla.txt" ); if( e == FileNotFound ) MessageUser( "File bla.txt not found" ); if( e == AccessDenied ) MessageUser( "Failed to open bla.txt, because we don't have read rights ..." ); if( e != Success ) MessageUser( "Failed due to some other error, error code = " + itoa( e ) ); try { std::vector<TObj> k( 1000 ); // ... } catch( const bad_alloc& b ) { MessageUser( "out of memory, exiting process" ); throw; }
不过,当你的库只抛出你自己的exception时,你可以使用exception规范来陈述你的意图。
避免C ++中的exception规范。 你给你的问题的原因是一个很好的开始,为什么。
参见赫特·萨特(Herb Sutter)的“exception规范的实用观点” 。
我认为除了约定之外的标准(C ++)
exception说明符是C ++标准中的一个大部分失败的实验。
这个例外是没有使用说明符是有用的,但你也应该在内部添加适当的try catch块,以确保代码匹配说明符。 香草萨特有关于这个问题的网页。 Gotch 82
另外我认为这是值得描述的例外保证。
这些基本上是关于如何逃避对象上的方法的exception影响对象的状态的文档。 不幸的是,它们没有被编译器强制执行或以其他方式提到。
提升和例外
例外保证
没有保证:
exception转义方法后,不能保证对象的状态
在这些情况下,不应再使用该物体。
基本保证:
在几乎所有情况下,这应该是方法提供的最低保证。
这保证了对象的状态已经被很好地定义,并且仍然可以被一致地使用。
有力的保证:(又名交易保证)
这保证了该方法将成功完成
或者抛出exception,对象状态不会改变。
不保证投票:
该方法保证不允许exception传播出该方法。
所有的析构函数都应该做这个保证。
| NB如果exception已经传播,exception转义析构函数
| 应用程序将终止
当违反exception规范时,gcc会发出警告。 我所做的只是使用macros来使用exception规范,只能在“lint”模式下进行明确的编译以确保exception符合我的文档。
唯一有用的exception说明符是“throw()”,如“不抛出”。
不,如果你使用它们,并且抛出了一个你没有指定的exception,通过你的代码调用你的代码或代码,那么默认的行为是立即终止你的程序。
另外,我相信在目前的C ++ 0x标准草案中,它们的使用已经被弃用了。
exception规范在C ++中并不是非常有用的工具。 但是,如果与std :: unexpected结合使用,那么对他们来说是非常有用的。
我在某些项目中做的是带有exception规范的代码,然后用一个函数调用set_unexpected(),这个函数会抛出我自己devise的特殊exception。 这个exception在构build时会得到一个回溯(以特定于平台的方式),并从std :: bad_exception派生(如果需要,可以传播它)。 如果它像通常一样导致terminate()调用,那么回溯是通过what()(以及导致它的原始exception,而不是很难find的)来打印的,所以我得到我的合同在哪里的信息违反,例如什么意外的库exception被抛出。
如果我这样做,我绝不允许传播库exception(std除外),并从std :: exception中派生所有的exception。 如果一个图书馆决定抛出,我会抓住并转换成我自己的层次结构,允许我总是控制代码。 调用相关函数的模板化函数应该避免exception规范,原因很明显; 但是很less有一个使用库代码的模板化函数接口(而且很less有库真正以有用的方式使用模板)。
如果您正在编写的代码将被那些宁愿查看函数声明的人使用,而不是其周围的任何注释,那么规范将会告诉他们哪些exception可能要被捕获。
否则,我不觉得特别有用,但使用throw()
来指示它不会抛出任何exception。
如果知道函数永远不会抛出exception(或者至less承诺永不抛出exception),则“throw()”规范允许编译器在执行代码stream分析时执行一些优化。 拉里奥斯特曼在这里简要地谈到:
http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx
通常我不会使用exception说明符。 但是,如果有任何其他例外来自相关函数,程序肯定无法更正 ,那么它可能是有用的。 在任何情况下,都要确保清楚地logging该function可能出现的exception情况。
是的,从具有exception说明符的函数抛出的非指定exception的预期行为是调用terminate()。
我也会注意到Scott Meyers用更有效的C ++来解决这个问题。 他的有效C ++和更有效的C ++是高度推荐的书籍。
是的,如果你是内部文件。 或者也许写一个别人可以使用的库,这样他们就可以在没有查阅文档的情况下知道发生了什么。 抛出或不抛出可以被认为是API的一部分,几乎就像返回值。
我同意,它们在编译器中强制执行Java风格并不是很有用,但它总比什么都没有,或者是随意的评论。
它们对于unit testing是有用的,所以在编写testing时你知道该函数在失败时会抛出什么东西,但是在编译器中没有围绕它们的强制。 我认为它们是C ++中不需要的额外代码。 你select哪一个你应该确定的是你在整个项目和团队成员中遵循相同的编码标准,以便你的代码保持可读性。
从文章:
http://www.boost.org/community/exception_safety.html
“众所周知,编写一个exception安全的通用容器是不可能的”。这种说法常常参考Tom Cargill的文章[4],其中他探讨了通用堆栈模板的exception安全问题。 嘉吉在他的文章中提出了很多有用的问题,但不幸的是他没有解决他的问题。他最后提出一个解决scheme可能是不可能的。 不幸的是,他的文章被许多人看作是这种猜测的“证据”。 自发布以来,已经有很多exception安全通用组件的例子,其中包括C ++标准库容器。
事实上,我可以想办法使模板类exception安全。 除非你不能控制所有的子类,否则你可能会遇到问题。 要做到这一点,你可以在你的类中创buildtypedef来定义各种模板类抛出的exception。 这个问题以前一直在讨论,而不是从一开始就devise的,我认为这是真正的障碍。
例外规格=垃圾,问任何30岁以上的Java开发者