为什么要重新抛出异常

时间:2014-11-26 14:54:19

标签: c++

在C ++中,为什么要重新抛出异常?为什么不让当前的catch块处理异常。出于什么原因,您会将异常重新抛出到另一个try / catch块?

3 个答案:

答案 0 :(得分:3)

当一个函数无法满足它的契约时它会引发一个异常(它会向调用者承诺它会做什么)。当一个函数调用另一个抛出异常的函数时,有四种主要方法可以响应:

  1. 抓住异常并处理。只有在函数能够满足其合同的情况下才能执行此操作,尽管抛出了异常。如果它捕获异常但未能满足其合同,则会隐藏调用代码中的问题。

  2. 允许异常传播。如果此函数无法处理异常(即函数因抛出异常而无法满足其约定),并且异常将相应的信息公开给调用代码,则应该这样做。

  3. 抓住异常,进行一些清理和/或添加额外信息,然后重新抛出。如果此函数无法处理异常,则应该执行此操作,但在传播之前需要进行一些清理。它还可以提供额外的信息来帮助处理/调试异常(我经常认为程序员是最后一个异常处理程序)。

  4. 抓住异常并抛出异常(可能包装原件)。如果此函数无法处理异常,则应该这样做,但是另一个异常更好地表达了调用代码的问题。

答案 1 :(得分:1)

  

为什么不让当前的catch块处理异常。出于什么原因,您会将异常重新抛出到另一个try / catch块?

异常背后的想法是你将它们扔到错误站点并在堆栈中处理它们,在那里你有足够的信息来处理错误。

相反,有些情况下您必须在出现错误时执行某些操作,但仍然不知道如何处理错误(当您重新抛出时就是这种情况)。

示例:

void connect_and_notify(int connection_data)
{
    try
    {
        create_network_connection(connection_data); // defined somewhere else
        notify("connection open");                  // same (notify event listeners)
    }
    catch(const std::runtime_error&)
    {
        notify("connection failed");
        throw;
    }
}

客户代码:

void terminal_app_controller()
{
    try
    {
        connect_and_notify(1);
    }
    catch(const std::runtime_error& err)
    {
        std::cerr << "Connection failed;\n";
        exit(1); // this is usually bad bad code but whatever
    }
}

void ongoing_server_controller()
{
    bool connected = false;
    int connection = 1;
    while(!connected)
    {
        try
        {
            connect_and_notify(1);
            connected = true;
        }
        catch(const std::runtime_error&)
        {
            connection++;
        }
    }
}

在两种使用方案中,错误的处理方式不同(connect_and_notify无法知道,但是,在失败的连接上,它必须通知侦听器。)

每个函数都有不同的策略来处理异常,这意味着不同的catch块。

答案 2 :(得分:0)

我非常不喜欢

之类的东西
catch (std::exception&) {
    ... // do some cleanup
    throw;
}

RAII是解决该问题的正确方法。甚至:

catch (std::exception&) {
    ... // do some logging here
    throw;
}

可以使用RAII处理,但不太直观。

但是 - 我重新抛出的地方是第3部分(或供应商提供的)代码抛出“泛型”异常状态的任何情况。例如,在将远程信息处理消息记录到数据库时,我知道我经常会收到同一消息的重复副本。每条消息都有一个唯一的ID - 因此我的数据库中的主键违规是一个“无辜的”错误,应该被忽略。

不幸的是,我们使用的DB框架没有为PK违规抛出特定的异常 - 所以我们需要捕获泛型dbexception并检查它的原因代码是什么来决定做什么。因此:

catch (db::exception& e) {
    if (e.reason != db::exception::reason::pk_violation)
        throw;
}

此外,piwi提到了内部状态。一个例子是

for (;;) {
    try {
        ...
    }
    catch (some_exception& e) {
        if (retry_count > 3)
            throw;
    }
}

请记住:如果要重新抛出,请始终通过引用捕获以避免切片异常对象。 (无论如何,你通常应该通过ref捕获它,但在重新抛出时更为重要)