当前位置:  开发笔记 > 编程语言 > 正文

我应该在C++中使用异常说明符吗?

如何解决《我应该在C++中使用异常说明符吗?》经验,为你挑选了5个好方法。

在C++中,您可以通过使用异常说明符指定函数可能会也可能不会抛出异常.例如:

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

由于以下因素,我对实际使用它们表示怀疑:

    编译器并没有以任何严格的方式真正强制执行异常说明符,因此效益并不高.理想情况下,您希望得到编译错误.

    如果函数违反了异常说明符,我认为标准行为是终止程序.

    在VS.Net中,它将throw(X)视为throw(...),因此遵守标准并不强.

你认为应该使用异常说明符吗?
请回答"是"或"否"并提供一些理由来证明您的答案.



1> Christopher..:

没有.

以下是几个例子:

    使用异常规范无法编写模板代码,

    template
    void f( T k )
    {
         T x( k );
         x.x();
    }
    

    副本可能会抛出,参数传递可能会抛出,并x()可能抛出一些未知异常.

    异常规范往往会禁止可扩展性.

    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();
    }
    

    glib_f()投掷时会终止.这(在大多数情况下)不是你真正想要的.std::terminate()永远不应该被称呼.最好让应用程序崩溃时出现未处理的异常,从中可以检索堆栈跟踪,而不是静默/暴力死亡.

    编写返回常见错误的代码,并在特殊情况下抛出.

    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 k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }
    

然而,当您的库只是抛出自己的异常时,您可以使用异常规范来陈述您的意图.


@Greg Rogers:一个未捕获的异常仍然会进行堆栈展开.这意味着将调用析构函数.在那些析构函数中,可以做很多事情,例如:资源正确释放,正确写入日志,其他进程将被告知当前进程崩溃等等.总而言之,它是RAII.
@paercebal那是不对的.实现定义是否针对未捕获的异常运行析构函数.如果未捕获异常,大多数环境都不会展开堆栈/运行析构函数.如果你想要可移植地确保你的析构函数运行,即使抛出异常并且没有处理(这是值得怀疑的),你需要编写代码,如`try {<< ... code ... >>} catch( ...)/*堆栈保证在这里解开并且dtors运行*/{throw;/*将它传递给运行时*/}`

2> Michael Burr..:

避免使用C++中的异常规范.你在问题中提出的原因是一个很好的开始.

参见Herb Sutter的"实用的异常规范".


@awoodland:在C++ 11中不推荐使用'dynamic-exception-specifications'(`throw(optional-type-id-list)`).它们仍处于标准状态,但我想已发出警告,要求仔细考虑使用它们.C++ 11添加了`noexcept`规范和运算符.我不太了解"noexcept"的足够细节来评论它.这篇文章似乎相当详细:http://akrzemi1.wordpress.com/2011/06/10/using-noexcept/和DietmarKühl在2011年6月的Overload Journal上发表了一篇文章:http://accu.org/var /uploads/journals/overload103.pdf

3> Martin York..:

我认为标准除了约定(对于C++)
异常说明符是C++标准中的一个实验,大多数都失败了.
例外情况是no throw说明符很有用,但你也应该在内部添加适当的try catch块以确保代码与说明符匹配.Herb Sutter有一个关于这个主题的页面.Gotch 82

另外我认为值得描述异常保证.

这些基本上是关于如何通过转义该对象上的方法的异常影响对象状态的文档.不幸的是,它们没有被编译器强制执行或以其他方式提及.
提升和例外

例外保证

无保证:

异常转义方法后,无法保证对象的状态
在这些情况下,不应再使用该对象.

基本保证:

在几乎所有情况下,这应该是方法提供的最小保证.
这可以保证对象的状态定义良好,并且仍然可以一致地使用.

强有力的保证:(又称交易保证)

这可以保证方法成功完成
或抛出异常并且对象状态不会改变.

无投保证:

该方法保证不允许任何异常传播出该方法.
所有析构函数都应该做出这种保证.
| NB如果一个异常逃离析构函数时异常已经传播
| 申请将终止



4> Jeremy..:

当您违反异常规范时,gcc将发出警告.我所做的是使用宏只在"lint"模式下使用异常规范明确编译以检查以确保异常与我的文档一致.



5> Harold Ekstr..:

唯一有用的异常说明符是"throw()",如"不抛出".


你能说一下它有用的原因吗?
为什么没用?没有什么比知道某些功能不会开始抛出左右中心的异常更有用了.
推荐阅读
牛尾巴2010
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有