C++ 在抛出期间构造对象时抛出异常?

C++ 在抛出期间构造对象时抛出异常?,c++,exception,C++,Exception,我有一个异常类: class MyException : public std::exception { public: MyException( char* message ) : message_( message ) { if( !message_ ) throw std::invalid_argument("message param must not be null"); } }; 在我的网站上: try { throw

我有一个异常类:

class MyException : public std::exception
{
public:
    MyException( char* message )
        : message_( message )
    {
        if( !message_ ) throw std::invalid_argument("message param must not be null");
    }
};
在我的网站上:

try {
    throw MyException( NULL );
}
catch( std::exception const& e ) {
    std::cout << e.what();
}
试试看{
抛出MyException(NULL);
}
捕获(标准::异常常量和e){

std::cout您要抛出的对象(在本例中是
MyException
)必须成功构造,然后才能抛出。因此您还没有抛出它,因为它尚未构造

因此,这将起作用,从
MyException
的构造函数中引发异常。您不会触发“在处理异常时引发异常导致
std::terminate
”问题。

15.1引发异常n3376 第7段 如果异常处理机制在完成对要抛出的表达式的求值之后但在捕获异常之前调用通过异常退出的函数,则调用std::terminate(15.5.1)

这意味着,在构造函数(本例中抛出的对象)完成之前,不会发生任何特殊情况。但在构造函数完成之后,任何其他未考虑的异常都将导致调用
terminate()

该标准还提供了一个示例:

struct C
{
       C() { }
       C(const C&) { throw 0; }
};

int main()
{
  try
  {
    throw C();   // calls std::terminate()
  }
  catch(C) { }
}
此处调用terminate是因为首先创建了对象。但随后调用copy构造将异常复制到保留位置(15.1.4)。在此函数调用(copy构造)期间,将生成未捕获的异常,因此调用terminate

因此,如图所示的代码应该按预期工作

  • 或者:生成带有良好消息的
    MyException
    ,并抛出
  • 或者:生成并抛出
    std::invalid_参数

如果要检查应始终为真的不变量,则应使用断言。异常是指预期会发生的异常情况,如极端情况或错误的用户输入

如果您使用异常来报告代码中的错误,您可能会意外地使用catch(…)隐藏它们。如果您正在编写库代码,这一点尤其重要,因为这样您就永远不知道其他人是否会捕获并忽略异常,即使这意味着系统已达到无效状态


断言的另一个优点是,如果您愿意,您可以完全禁用它们,这样它们就不会再招致任何性能损失。这使得您在调试版本中对这些检查非常偏执,并且仍然有一个闪电般快速的发布版本。

只要您在某个地方发现它……您考虑过使用
c吗onst std::string&message
所以这不是问题?如果您从std::runtime_错误派生,则不需要自己存储消息。将mssage传递给std::runtime_错误的构造函数。注意:一些流行但错误的实现允许您将消息传递给std::exception。不幸的是,这不符合标准,并且会导致移植时出现问题。@LokiAstari我不使用
std::runtime\u error
(即使我愿意),因为它需要在构造过程中形成完整的
what()
字符串。我喜欢在
what()之前对该字符串进行处理
被调用,因此我使用
std::exception
来实现此目的。@RobertDailey:我不明白你在说什么。std::exception不支持对字符串.PS进行延迟计算。我还认为这是一个错误的优化。@LokiAstari你误解了。我只是在post pone上执行我的字符串构建逻辑il what()被称为.std::runtime_error,它在内部存储一个字符串,并且在其构造函数中需要一个字符串。这几乎迫使您为what()构建字符串当你构造你的异常对象时。有意义吗?我试图在标准中找到它,但我什么也没看到。2003年标准中提到过这种特殊情况吗?@RobertDailey:没有;为什么会这样?在
throw
可以执行之前,它必须首先成功地计算它的表达式。在
throw
执行之前,什么都没有G已经被抛出了。这个表达式没有成功地评估,所以投掷从来没有发生过。这是C++中许多独立部分不可避免的结果。@尼科尔博拉斯:你的假设是正确的,但我不相信这个论点。一旦
throw语句的任何部分启动(throw语句包括throw表达式),即在从上一个语句跨越
序列点之后,就启动了throw。幸运的是,标准在这个问题上是明确的,请参见:15.1.7(在n3376中)断言的缺点是,它们通常在运行时被禁用。有时,即使它应该始终为真,您仍然希望在运行时检查它,即使在发布模式下也是如此。如果程序员希望使用
catch(…)隐藏所有异常
,那么这就是糟糕的编程实践。您还可以反对
断言
,因为这通常是作为退出调用(或某种变体)实现的,我可以注册一个要在退出时调用的函数,该函数只会跳回正常执行,并以这种方式隐藏错误。您无法防御有权访问代码并与您对抗的程序员。您不必使用标准assert()宏,您也可以定义自己的。这样,您可以在发布版本中禁用断言,其他断言也应该保留。如果您引发异常,那么它应该是接口的文档部分,任何调用您的函数或构造函数来引发异常的人都必须考虑如何处理它在OP中,除了告诉用户程序有错误并崩溃外,无法以任何合理的方式处理所描述的问题。如果没有合理的处理方法,也不应该是例外。总是有合理的处理方法。我有一个程序可以搜索g