如何捕获构造函数异常?

时间:2016-08-02 08:26:43

标签: 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;
}

我不想从x.doSomething()只捕获构造函数中捕获异常。

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

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

7 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分: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.*/
    }
}

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

答案 2 :(得分:5)

是的,如果你把所有代码都放在icon子句中,例如使用function try block(以避免不必要的嵌套和范围),这是可能的:

FlatButton

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

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

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

正如霍尔特所指出的,这也将从try电话中捕获异常。有两种方法可以解决这个问题:

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

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

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

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

    如果您仍想从构造函数中抛出异常,实际上只有第三种方法可以捕获这些:使用其中一个代码示例顶部,并仅抛出void real_foo() { Bar x; x.doSomething(); } bool foo() try { real_foo(); return true; } catch (std::exception const& e) { return false; } 永远不能抛出的特定异常只捕获这些特定的构造函数。

答案 3 :(得分:2)

您必须在

的变体之间进行选择
bool foo() {
    std::unique_ptr<Bar> x;
    try {
        x = std::make_unique<Bar>();
    } catch (const BarConstructorException& e) {
        return false;
    }
    x->doSomething();
    return true;
}

bool foo() {
    try {
        Bar x;
        x.doSomething();
    } catch (const BarConstructorException& e) {
        return false;
    }
    return true;
}

答案 4 :(得分:2)

通常,如果要避免堆分配,则不能将局部变量的声明与其定义分开。因此,如果您要将所有内容合并到一个函数中,则必须使用public void onBackPressed(){ if (isSomeViewShowing) { hideSomeView(); } } 块围绕x的整个范围:

try/catch

答案 5 :(得分:2)

没有。从您的Java示例中,您必须在以下两种可能性中进行选择:

没有指针:

bool foo() {
    try {
        Bar x;
        x.doSomething();
    } catch (Exception e) {
        return false;
    }
    return true;
}

使用指针:

bool foo() {
    Bar* x = nullptr;
    try {
        x = new Bar();
    } catch (Exception e) {
        return false;
    }
    x->doSomething();

    delete x; // don't forget to free memory

    return true;
}

或使用托管指针:

#include <memory>

bool foo() {
    std::unique_ptr<Bar> x;
    try {
        x = new Bar();               // until C++14
        x = std::make_unique<Bar>(); // since C++14
    } catch (Exception e) {
        return false;
    }
    x->doSomething();
    return true;
}

答案 6 :(得分:1)

在修订后的问题中,OP增加了

的要求
  

我不想从x.doSomething()捕获异常,只捕获局部变量的构造函数。

翻译Java代码的简单方法

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

...到C ++,然后使用Optional_类(如Barton-Nackmann Fallibleboost::optional或C ++ 17 std::optional

auto foo()
    -> bool
{
    Optional_<Bar> xo;
    try
    {
        xo.emplace();
    }
    catch( ... )
    {
        return false;
    }

    Bar& x = *xo;
    // Possibly other code here, then:
    x.doSomething();
    return true;
}

一个不错的选择是重构代码,如下所示:

struct Something_failure {};

void do_something( Bar& o )
{
    // Possibly other code here, then:
    o.doSomething();
}

auto foo()
    -> bool
{
    try
    {
        Bar x;

        do_something( x );
        return true;
    }
    catch( Something_failure const& )
    {
        throw;
    }
    catch( ... )
    {}
    return false;
}

如果您不喜欢上述方法,那么您可以随时选择动态分配的Bar实例,例如使用std::unique_ptr进行保证清理,但是它具有动态分配的一般开销。在Java中,大多数对象都是动态分配的,因此可能看起来不是一个严重的缺点。但是在C ++中,大多数对象都是超高速堆栈分配,因此与普通操作相比,动态分配是非常慢的操作,因此动态分配的概念简单性必须加权。