当未定义异常变量时,通过引用捕获 [英] Catch by reference when exception variable is not defined

查看:167
本文介绍了当未定义异常变量时,通过引用捕获的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


  1. 如果异常是由于内存溢出异常抛出,我们不会

  2. 如果异常是继承父类的一部分,我们可能会对该异常进行对象切片。

如果我们有一个场景,我们没有在catch块中定义异常名称这些问题(真的1.,因为切片不会是一个问题if我们没有变量的名称)仍然有效?



例如:

  catch(my_exception)
{...}



  catch(my_exception&)
{...}

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



注意:我问这是因为我不得不检查某人的代码,在这种情况下的价值。如问题所示,我不完全确定任何一种选择的技术影响,但我认为在一致性方面,最好是在这种情况下通过引用来捕获(在任何情况下都没有任何缺点, 。

解决方案

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


N3290§15.3/ 16

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


上面的段落没有提到通过引用捕获,因此可以合理地断定它适用于异常对象是否被引用捕获;



但是,这与下一段是矛盾的:


N3290§15.3/ 17

当处理程序声明非常量对象时,对该对象的任何更改都不会影响临时
对象通过执行 throw-expression 初始化。当处理程序声明对
a非常量对象的引用时,对引用对象的任何更改都是在执行 throw-expression 时初始化为
的临时对象的更改,


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

  catch(T& name)

  catch(T&)$但是,我总是捕获例如 catch(T const&) 

code>。从实际的角度来看,可以假设编译器将优化到对抛出对象的直接引用,即使可以设计观察到的程序效果将是非标准的情况。例如< evil grin>

  #include< stdio。 h。 
#include< stdexcept>

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

char const * pMessage_;
错误()
:std :: runtime_error(基类消息)
,pMessage_(原始消息)
{
printf构造Error对象。\\\
);
pThrown = this;
}

错误(错误const&其他)
:std :: runtime_error(other)
,pMessage_(other.pMessage_)
{
printf(Copy-construction of Error obejct.\\\
);
}

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

错误*错误:: pThrown = 0;

int main()
{
printf(Testing non-const ref:\\\
);
try
{
throw错误();
}
catch(Error& x)
{
错误:: pThrown-> pMessage_ =修改的消息。
printf(%s\\\
,x.what());
}

printf(\\\
);
printf(Testing const ref:\\\
);
try
{
throw Error();
}
catch(错误const& x)
{
错误:: pThrown-> pMessage_ =修改的消息;
printf(%s\\\
,x.what());
}
}

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

 
测试非const引用:
默认构造Error对象。
修改后的消息。

测试const引用:
默认构造Error对象。
修改后的消息

一个拙劣的形式主义者可能会说两个编译器都不符合,无法为错误const& 案例。一个纯实际的实践者可能会说,嘿,你还期望什么??和我,我说,标准中的措辞是非常完美的在这里,如果有什么,应该期望明确允许上述输出的澄清,所以也通过引用捕获 const 既安全又最有效率。



总结wrt。 OP的问题:




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


  • 该标准仅保证参考非 const


  • 在实践中,如图所示,即使程序结果受到影响,也保证引用 const




hth。,


When catching an exception the standard guidance is to throw by value, catch by reference. As I understand it, this is for two reasons:

  1. If the exception was thrown due to an out of memory exception, we won't call a copy constructor which could potentially terminate the program.
  2. If the exception is part of an inheritance heirarchy, we may potentially have object slicing on the exception.

If we have a scenario where we don't define the exception name in the catch block are these concerns (really 1., as slicing won't be an issue if we don't have a name for the variable) still valid?

For example:

catch(my_exception)
{ ... }

or

catch(my_exception &)
{ ... }

Is there still the possibility of the program terminating if the exception caught by value in this case? My feeling is that it is technically still possible.

Note: I am asking this because I have had to review someone's code who put a catch by value in this case. As shown in the question I am not entirely sure on the technical impact of either choice, but I think that in terms of consistancy it is better to catch by reference in this case regardless (there is no downside to catching by reference in any case).

解决方案

The standard does not require special optimization in the case of an unnamed exception object. On the contrary, it then requires an effect as if a temporary is copy-initialized. This copying can result in memory being dynamically allocated.

N3290 §15.3/16:
If the exception-declaration specifies a name, it declares a variable which is copy-initialized (8.5) from the exception object. If the exception-declaration denotes an object type but does not specify a name, a temporary (12.2) is copy-initialized (8.5) from the exception object. The lifetime of the variable or temporary ends when the handler exits, after the destruction of any automatic objects initialized within the handler.

The paragraph above does not mention catching by reference, and so one might reasonably conclude that it applies whether or not the exception object is caught by reference; that a copy is constructed anyway.

However, that is contradicted by the next paragraph:

N3290 §15.3/17:
When the handler declares a non-constant object, any changes to that object will not affect the temporary object that was initialized by execution of the throw-expression. When the handler declares a reference to a non-constant object, any changes to the referenced object are changes to the temporary object initialized when the throw-expression was executed and will have effect should that object be rethrown.

So, declared type T& (with T non-const) is the single case where C++11 requires a reference directly to the thrown object, instead of copying. And it is also that way in C++03, except that C++03 has some additional wording about as-if optimization. So, for the formal the preference should be for

    catch( T& name )

and

    catch( T& )

However, I have always caught exceptions like catch( T const& ). From a practical point of view one may assume that the compiler will optimize that to a direct reference to the thrown object, even though it is possible to devise cases where the observed program effect would then be non-standard. For example <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() );
    }
}

With both MinGW g++ 4.4.1 and Visual C++ 10.0 the output is …

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

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

A pedantic formalist might say that both compilers are non-conforming, failing to create a copy for the Error const& case. A purely practical practitioner might say that hey, what else did you expect? And me, I say that the wording in the standard is very far from perfect here, and that if anything, one should expect a clarification to explicitly allow the output above, so that also catching by reference to const is both safe and maximally efficient.

Summing up wrt. the OP's question:

  • Catching by reference won’t call a copy constructor which could potentially terminate the program.

  • The standard only guarantees this for reference to non-const.

  • In practice, as shown, it is also guaranteed for reference to const, even when program results are then affected.

Cheers & hth.,

这篇关于当未定义异常变量时,通过引用捕获的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆