并非所有控制路径都返回值?警告

时间:2013-04-23 18:00:39

标签: c++ error-handling compiler-warnings

我有一小段代码在编译时给出了以下警告:

' BGOLUB ::容器::堆栈::流行' :并非所有控制路径都返回值

以下是代码:

template<typename T>
T Stack<T>::Pop()                                                                           
{

try
{
    if (m_index<0) throw OutOfBoundsException(m_index);

    --m_index;
    return(m_array[m_index]);
}

catch(OutOfBoundsException&)
{
    cerr<<"Underflow Index = "<<m_index<<endl;
}

catch(...)
{
    cerr<<"Unhandled Error Occured"<<endl;
}
}

有什么建议吗?

非常感谢!

4 个答案:

答案 0 :(得分:18)

  

有什么建议吗?

编译器会给你最好的建议。并非函数中的所有控制路径都包含return语句,并且您的函数应该返回一个值。

如果抛出异常并将控制转移到catch处理程序,该处理程序将向cerr打印一些内容,然后从函数末尾流出而不实际return任何东西

这是未定义的行为。根据C ++ 11标准的第6.6.3 / 2段:

  

[..]流出函数的末尾相当于没有值的返回; 这导致未定义   价值回归函数中的行为

对于默认可构造的值,您可以通过在函数结束前添加return T()语句来解决此问题:

template<typename T>
T Stack<T>::Pop()
{
    try
    {
        // ...
    }
    catch (OutOfBoundsException&)
    {
        // ...
    }
    catch (...)
    {
        // ...
    }

    return T();
//  ^^^^^^^^^^^
}

然而,更合理的方法是Pop()吞下异常,而是重新抛出Pop()没有关于如何从此上下文中发生的错误中恢复的战略级信息:

template<typename T>
T Stack<T>::Pop()
{
    try
    {
        // ...
    }
    catch (OutOfBoundsException&)
    {
        // ...
        throw; // <== Re-throw after printing the diagnostic
    }
    catch (...)
    {
        // ...
        throw; // <== Re-throw after printing the diagnostic
    }
}

如果记录错误消息的责任根本不属于Pop(),那就更好了,因为Pop()可能应该被具有不同要求的代码重用(在这种意义上)有些人可能不想记录任何东西,有些人可能想要将消息记录到文件中,有些人可能想要用不同的语言记录消息,等等。

因此,更合理的函数版本实际上是:

template<typename T>
T Stack<T>::Pop()                
{
    if (m_index<0) throw OutOfBoundsException(m_index);
    --m_index;
    return(m_array[m_index]);
}

一般情况下,你应该尝试(没有双关语)以避免try/catch阻止,除非你必须:

  • 翻译例外
  • 从错误中恢复(但您需要具备战略知识)

如果这不是您的任务(就像上面的Pop()这样的函数的情况),在大多数情况下,最好的办法是根本不处理异常并让它们传播调用堆栈。

引用Dave Abrahams

  

处理异常的最佳方法通常是根本不处理它们。如果您可以让他们通过您的代码并允许析构函数处理清理,那么您的代码将更加清晰。

为避免泄露内存,资源或一般职责,请使用足够的 RAII包装编写异常安全代码。在this two-part talk by Jon Kalb中给出了这种意义上的优秀指南。

特别是,避免编写catch (...)处理程序:发明异常是为了防止程序员忽略错误,并且在没有重新抛出它们的情况下将它们全部吞没在通用处理程序中是忽略它们的最佳方法。


注意:

请注意,Pop()的实现有点问题:如果复制构造函数或T的移动构造函数在将元素返回给调用者后抛出,在您已经修改了堆栈之后会发生什么指针?

这就是为什么C ++标准库定义了两个单独的函数pop()top():因为它允许提供强保证,即为您提供事务语义pop()操作 - 删除元素时不会抛出异常,或者函数根本没有效果。

答案 1 :(得分:1)

你需要重新抛出异常,或者在函数末尾返回一些可能是T()的东西。

答案 2 :(得分:1)

抛出异常时,它将被两个catch语句中的一个捕获。但是,它们仍需要return函数的值。您可以在函数末尾放置return语句。

但是,如果Pop因为Stack为空而抛出异常,则让异常传播出函数更有意义。为什么Pop本身试图处理异常情况?

答案 3 :(得分:0)

我建议在所有if语句中使用括号,即使是单行符号也是如此。它们不是绝对必要的,你可以在没有它们的情况下编写完全合法的代码,但是它们使你的代码更具可读性,并且这样的错误将更容易找到。

此外,您似乎对异常的工作方式存在根本性的误解。如果您的代码遇到异常,它将直接跳转到catch块,而不执行try块中的任何后续代码。因此,return块中的try语句永远不会被访问,并且您的函数不会返回任何内容,因为catch块缺少return语句。

您可以在return块中添加catch语句来解决此问题。