如何捕获构造函数异常?

And*_*Sun 10 c++ constructor exception-handling

我有一个C++类,它会在构造函数失败时抛出异常.如何保持此类的本地实例(不使用new)并处理任何可能的异常,同时保持try块范围尽可能小?

基本上,我正在寻找以下Java习惯用语的C++等价物:

boolean foo() {
    Bar x;
    try {
        x = new Bar();
    } catch (Exception e) {
        return false;
    }
    x.doSomething();
    return true;
}
Run Code Online (Sandbox Code Playgroud)

我不想捕获异常x.doSomething(),只有构造函数.

我想我正在寻找的是一种分离声明和初始化的方法x.

是否可以在不使用堆分配和指针的情况下实现此目的?

son*_*yao 12

你可以使用C++ 17中的std :: optional:

bool foo() {
    std::optional<Bar> x; // x contains nothing; no Bar constructed
    try {
        x.emplace();      // construct Bar (via default constructor)
    } catch (const Exception& e) {
        return false;
    }
    x->doSomething();     // call Bar::doSomething() via x (also (*x).doSomething() )
    return true;
}
Run Code Online (Sandbox Code Playgroud)


Bat*_*eba 6

这个Java习惯用法不能很好地转换为C++,因为即使你的真实构造函数需要传递参数,它Bar x;也需要 默认的构造函数.

我建议在这个程度上对抗语言 - 扩大try块就足够了 - 但是如果你真的想要缩小那么你可以使用一个函数并依靠返回值优化来避免值复制:

Bar foobar()
{
    try {
        return Bar();
    } catch (Exception& e){
        /* Do something here like throwing a specific construction exception
           which you intercept at the call site.*/
    }
}
Run Code Online (Sandbox Code Playgroud)

但实际上,你可以在构造上抛出一个特定的异常,因此完全避免使用这个函数.


Som*_*ude 5

是的,如果您将所有代码放在try子句中,例如通过使用函数try块(以避免不必要的嵌套和作用域),这是可能的:

bool foo() try
{
    Bar x;
    x.doSomething();
    return true;
}
catch (std::exception const& e)
{
    return false;
}
Run Code Online (Sandbox Code Playgroud)

或者在try子句中调用另一个执行实际工作的函数:

void real_foo()
{
    Bar x;
    x.doSomething();
}

bool foo() try
{
    real_foo();
    return true;
}
catch (std::exception const& e)
{
    return false;
}
Run Code Online (Sandbox Code Playgroud)

请注意,在构造函数中抛出异常通常不是一个好主意,因为这将停止对象的构造,并且不会调用其析构函数.


正如霍尔特所指出的,这也将捕获doSomething呼叫中的异常.有两种方法可以解决这个问题:

  1. 简单而标准的方法:使用指针.

  2. 使用两阶段构造:拥有一个不能抛出异常的默认构造函数,然后调用一个可以抛出异常的特殊"构造"函数.

第二种方式在C++标准化之前很常见,并广泛用于Symbian系统的代码中.它不再常见,因为使用指针更容易和更简单,特别是今天可用的智能指针很好.我真的不推荐现代C++中的第二种方式.

最简单的方法当然是确保构造函数根本不能抛出异常,或者如果抛出任何异常,那么它们具有程序无法继续并且程序终止的性质.正如您对问题的评论中所指出的,C++中的异常是昂贵的,然后我们也有废弃的构造问题,并且在所有使用C++中的异常时,只应在特殊情况下进行.C++不是Java,即使两种语言都有类似的结构,也不应该这样对待它.

如果你仍然想从构造函数中抛出异常,实际上只有第三种方法可以捕获那些:使用其中一个代码示例up top,并且只doSomething抛出永远不能抛出的特定异常,然后只捕获这些特定的构造函数.

  • 这不会模仿给定java代码的行为,因为你将从`x.doSomething()`中捕获异常. (2认同)
  • @Holt"不会模仿给定java代码的行为"在用C++编写时,人们通常想用惯用的C++编写而不是模仿其他语言. (2认同)