未定义异常变量时按引用捕获

时间:2011-09-13 11:42:10

标签: c++ exception

当捕获异常时,标准指导是按值抛出,通过引用捕获。据我了解,这有两个原因:

  1. 如果由于内存不足异常而引发异常,我们将不会调用可能会终止程序的复制构造函数。
  2. 如果异常是继承层次结构的一部分,我们可能会对异常进行对象切片。
  3. 如果我们有一个我们没有在catch块中定义异常名称的场景是这些问题(实际上是1.,因为如果我们没有变量的名称,切片将不会成为问题)有效?

    例如:

    catch(my_exception)
    { ... }
    

    catch(my_exception &)
    { ... }
    

    如果在这种情况下由值捕获的异常,程序是否仍有可能终止?我的感觉是技术上仍然可行。

    注意:我问这个是因为我必须审查某人的代码,他们在这种情况下按值列出了值。正如问题所示,我不完全确定任何一种选择的技术影响,但我认为就稳定性而言,最好在这种情况下通过引用而不管(在任何情况下都没有引用的依据)

3 个答案:

答案 0 :(得分:5)

在未命名的异常对象的情况下,标准不需要特殊优化。相反,它需要一个效果,好像临时是复制初始化。这种复制可能导致内存被动态分配。

  

N3290§15.3/ 16
  如果 exception-declaration 指定了一个名称,它会声明一个从中复制初始化(8.5)的变量。   异常对象。如果 exception-declaration 表示对象类型但未指定名称,则从异常对象复制初始化(8.5)临时(12.2)。变量或临时的生命周期   在处理程序中初始化的任何自动对象的销毁之后,处理程序退出时结束。

上面的段落没有提到引用,因此可以合理地得出结论,无论异常对象是否被引用捕获,它都适用;无论如何都要构建一个副本。

然而,这与下一段相矛盾:

  

N3290§15.3/ 17
  当处理程序声明一个非常量对象时,对该对象的任何更改都不会影响临时对象   通过执行 throw-expression 初始化的对象。当处理程序声明对引用的引用时   非常量对象,对引用对象的任何更改都是对已初始化的临时对象的更改   当 throw-expression 被执行时,如果该对象被重新抛出则会生效。

因此,声明类型T&T非 - const)是C ++ 11需要直接引用抛出对象而不是复制的单例。在C ++ 03中也是这样,除了C ++ 03有一些关于as-if优化的附加措辞。因此,对于正式,首选应该是

    catch( T& name )

    catch( T& )

但是,我总是遇到像catch( T const& )这样的例外情况。从实际的角度来看,可以假设编译器将优化它以直接引用抛出的对象,即使可以设计观察到的程序效果将是非标准的情况。例如<evil grin>

#include <stdio.h>
#include <stdexcept>

struct Error
    : std::runtime_error
{
public:
    static Error* pThrown;

    char const* pMessage_;
    Error()
        : std::runtime_error( "Base class message" )
        , pMessage_( "Original message." )
    {
        printf( "Default-construction of Error object.\n" );
        pThrown = this;
    }

    Error( Error const& other )
        : std::runtime_error( other )
        , pMessage_( other.pMessage_ )
    {
        printf( "Copy-construction of Error obejct.\n" );
    }

    char const* what() const throw() { return pMessage_; }
};

Error*  Error::pThrown  = 0;

int main()
{
    printf( "Testing non-const ref:\n" );
    try
    {
        throw Error();
    }
    catch( Error& x )
    {
        Error::pThrown->pMessage_ = "Modified message.";
        printf( "%s\n", x.what() );
    }

    printf( "\n" );
    printf( "Testing const ref:\n" );
    try
    {
        throw Error();
    }
    catch( Error const& x )
    {
        Error::pThrown->pMessage_ = "Modified message";
        printf( "%s\n", x.what() );
    }
}

使用MinGW g ++ 4.4.1和Visual C ++ 10.0,输出为......

Testing non-const ref:
Default-construction of Error object.
Modified message.

Testing const ref:
Default-construction of Error object.
Modified message

一个迂腐的形式主义者可能会说两个编译器都不符合要求,没有为Error const&案例创建副本。一个纯粹实际的从业者可能会说嘿,你还有什么期望?而我,我说标准中的措辞在这里非常完美,如果有的话,人们应该期望澄清明确允许上面的输出,所以通过引用const也是安全的并且最有效率。

总结一下。 OP的问题:

  • 通过引用捕获不会调用可能会终止程序的复制构造函数。

  • 标准仅保证此参考非const

  • 在实践中,如图所示,即使计划结果受到影响,也可以保证参考const

干杯&amp;第h。,

答案 1 :(得分:1)

我更愿意通过引用来捕捉。编译器可以将异常作为优化丢弃并且不进行复制,但这只是可能性。通过引用捕获可以确定。

答案 2 :(得分:1)

可能存在与您的例外相关联的不可见数据,例如一个vtable

vtable是一个不可见的数据结构,它携带有关可以找到某些多态(即virtual)成员函数的位置的信息。在一般情况下,该表花费了对象本身中的一些内存。这可能是通过指针的大小进入一些外部表,甚至是完整的表。一如既往,这取决于。