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

Byr*_*ron 3 c++ error-handling compiler-warnings

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

'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;
}
}
Run Code Online (Sandbox Code Playgroud)

有什么建议?

非常感谢!

And*_*owl 18

有什么建议?

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

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

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

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

对于default-constructible值,您可以通过return T()在函数结束前添加语句来解决此问题:

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

    return T();
//  ^^^^^^^^^^^
}
Run Code Online (Sandbox Code Playgroud)

较为合理的做法是,然而,具有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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

所以一个更合理的函数版本实际上是:

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

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

  • 翻译例外
  • 从错误中恢复(但是你需要战略知识才能做到这一点)

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

引用Dave Abrahams:

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

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

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


注意:

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

这就是为什么在C++标准库定义了两个独立的功能pop()top():因为它允许提供强有力的保证,即给事务语义到您的pop()操作-无论是元素没有异常去除被抛出,或功能无影响可言.

  • 很好的答案. (3认同)