4

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

'BGOLUB::Containers::Stack::Pop' :并非所有控制路径都返回值

这是代码:

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

4 回答 4

19

有什么建议吗?

编译器给你最好的建议。并非函数中的所有控制路径都包含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()上面的函数一样),在大多数情况下,最好的办法是根本不处理异常,让它们向上传播调用堆栈。

引用戴夫亚伯拉罕的话:

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

为避免泄漏内存、资源或一般责任,请使用适当的RAII 包装器编写异常安全的代码。Jon Kalb 在这个由两部分组成的演讲中给出了这个意义上的优秀指导方针。

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


笔记:

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

这就是 C++ 标准库定义两个独立函数的原因,pop()并且top():因为它允许提供强有力的保证,即为您的pop()操作提供事务语义 - 要么删除元素而不抛出异常,要么函数根本没有效果.

于 2013-04-23T18:01:40.593 回答
1

您需要重新抛出异常,或者在函数结束时返回可能 T() 的东西。

于 2013-04-23T18:02:21.377 回答
1

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

但是,如果Pop由于 为空而引发异常Stack,则让异常传播到函数之外更有意义。为什么Pop自己试图处理异常情况?

于 2013-04-23T18:02:21.877 回答
0

我建议在您的所有if陈述中使用方括号,即使是单行正文。它们不是绝对必要的,没有它们你可以编写完全合法的代码,但是它们使你的代码更具可读性,并且这样的错误会更容易发现。

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

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

于 2013-04-23T18:08:51.350 回答