C ++:如何捕获构造函数抛出的异常?

时间:2017-07-28 09:41:20

标签: c++ exception-handling

我有一个类,称之为A,其构造函数接受一些输入参数,如果它们与构造该对象不兼容,则可能抛出异常。在我的main代码中,我构造了一个A类型的对象,如下所示:

A my_obj(arg1,arg2,arg3);

并使用它。显然,如果构造函数失败并抛出异常,则在打印出“未处理的异常”消息后,程序的执行将被终止。

但是,我想在这种情况下向用户提供更多信息,并告诉他/她为什么抛出了exception。所以,我需要一种方法来catch例外。

为此,一种可能性是封装整个代码,从my_obj的声明开始直到try块中的程序结束,然后catch异常:

try {
    A my_obj(arg1, arg2, arg3);
    // ... 
    // about 100 other lines of code being executed if my_obj is created properly 
}
catch (std::exception& e) {
    // print a user-friendly error message and exit
}

但这对我来说有点“矫枉过正”。特别是因为在剩余的100行中没有抛出其他异常。有没有其他更好的方法来实现这一目标?

5 个答案:

答案 0 :(得分:2)

如果构造函数抛出,则表示没有对象。 std::optional<>是一种意味着&#34;我们这里可能没有对象的类型&#34;。

template <typename T, typename ... Args>
std::optional<T> try_make(Args&& ... args)
{ try {
    return make_optional(std::forward(args...));
} catch (...) {
    return {};
} }

然后

auto my_obj = try_make<A>(arg1,arg2,arg3);
if (my_obj) {
    // about 100 other lines of code being executed if my_obj is created properly
}

答案 1 :(得分:1)

一种可能性是使用指针(更好地使用智能指针,如下面的代码中的unique_ptr)。您可以将String.format()留空,在try块中调用构造函数并将指针移动到unique_ptr。之后,您的其他代码将执行。当然,您必须在简单的if语句中检查operator bool unique_ptr的有效指针。
为简化unique_ptr的使用,我们会引用my_obj

A& my_obj_ref = *my_obj;

请记住,这种方式会将您的对象分配到堆而不是堆栈。

答案 2 :(得分:0)

您可以将对象构造抽象为捕获异常的函数:

declare
  cursor c1 is
    select 'R' PRD_ID, 'A' COMP, 'Y' TD1, 'Y' TD2, 'Y' TD3 from dual;
  cursor c2 is
    select 'S' ACC_ID, 'A' COMP, 'Y' TD1, 'Y' TD2, 'Y' TD3 from dual;
  c1_row c1%rowtype;
  c2_row c2%rowtype;
  FUNCTION test_equal (c1_row c1%rowtype, c2_row c1%rowtype) RETURN BOOLEAN
  IS
  BEGIN
    IF  c1_row.td1 = c2_row.td1
    AND c1_row.td2 = c2_row.td2
    AND c1_row.td3 = c2_row.td3
    THEN RETURN TRUE;
    ELSE RETURN FALSE;
    END IF;
  END;
begin
  open c1;
  open c2;
  loop
    fetch c1 into c1_row;
    exit when c1%notfound;

    fetch c2 into c2_row;
    exit when c2%notfound;

    if test_equal(c1_row, c2_row)
    then
      -- do something 
    end if;
  end loop;
end;

如果构造失败,上面会使用您的程序退出的事实。如果要求继续运行,该函数可以返回template<typename... Args> A make_a(Args&&... args) { try { return A(std::forward(args)...); } catch (std::exception& e) { // print a user-friendly error message and exit ... std::exit(EXIT_FAILURE); } } // ... in the actual code: A my_obj = make_a(arg1, arg2, arg3); (如果您无法访问C ++ 17,则可以返回其等效值。)

答案 3 :(得分:0)

这里有几个选项,具体取决于如果构造失败,您希望控制如何继续。

如果你想通过抛出异常退出函数,那么你不需要做任何事情,你可以让A构造异常传播开来。

如果要通过抛出不同的异常退出,或者在让A构造异常传播之前执行某些操作,则使用执行这些操作的工厂函数(可能是lambda),例如:< / p>

auto a_factory(T x, U y) -> A     // or use perfect forwarding
{
    try { return A(x, y);  }
    catch(...) {
         log("constructing A failed...");
         throw other_exception();
    } 
}

// ... 
     A my_obj = a_factory(x, y);        

如果要通过返回值退出,则仍然可以使用上述方法,但将调用函数包装在另一个捕获预期异常的函数中并返回一个值。

或者您可以使用optional(下方)或unique_ptr(由其他答案涵盖)技术,但可以从return块执行catch语句。< / p>

如果你想在没有有效A的情况下继续执行,那么你可以这样做:

std::optional<A> opt_my_obj;
try
{
    A temp(...args...);
    opt_my_obj.swap(temp);
} catch(...)
{
    // handling, you could return from the function here
}

// At this point you can test `if ( opt_my_obj )` to branch the flow.
// When you're at a point where you have verified the object exists, you 
// can enable normal object syntax by writing:

A& my_obj = *opt_my_obj;

如果您的函数中有多个对象需要考虑这些问题,我倾向于建议将整个函数包装在try ... catch中的版本可以处理所有不同的异常。

答案 4 :(得分:0)

我倾向于这样做简单:抛出人类可读的消息。当没有选择时,这种策略很有效,而且通常没有。虽然有一个问题,你希望异常处理相当健壮,所以我将消息打包在std::array<char,4096>必要时截断并记住零终止符(我知道这可能会破坏堆栈但它应该没问题如果我们不在递归函数中),那就扔掉它。

示例:

try
    {
    Options opts(argv);
    SomeResource resource(opts.someParameter());
    //...More actions that could throw
    }
catch(const std::array<char,4096>& errmessage) //Or rather some other type that contains the message.
    {
    fprintf(stderr,"Error: %s\n",errmessage.data());
    return -1; //Or any non-zero value
    }
return 0;

优点:

  • 快速为新类实现新的构造函数,因为只有一个异常类,这将适用于所有内容
  • 您将直接从源
  • 获取任何系统消息

缺点:

  • 缺乏上下文:消息必须说“不可能打开文件foo:没有这样的文件或目录。”。不告诉用户异常的根本原因。此问题继承自异常模型,如果不将异常视为美化错误代码,则无法解决此问题

  • 如果要对异常内容进行分支,则必须解析该消息,但我发现这很少需要。可能在编译器的上下文中,但无论如何都会打印该消息{.1}}。