修复退货问题.警告:控件到达C++中非void函数的末尾

Fix a return issue.Warning: control reaches end of non-void function in C++

本文关键字:void 中非 C++ 函数 问题 警告 控件      更新时间:2023-10-16

我知道这看起来可能是重复的,但其他问题没有我想要的答案。

template <class T>
T Queue<T>::pop_back()
{
    try
    {
        if (empty())
            throw "The Queue is empty!";
        size--;
        back= (back- 1 + max_size) % (max_size);
        return queue[back];
    }
    catch (char* strException)
    {
        cerr << "Error: " << strException << endl;
    }
}

我有上面pop_back函数的实现。每次删除元素时,都必须返回该元素。然而,每当它试图从空队列中弹出某个内容时,它都会说"在抛出'char const*'的实例后调用terminate"中止(堆芯转储)"

有什么技巧和建议可以解决这个问题?我在那里有一个尝试接球的盖帽,但没有多大帮助。函数必须从调用它的地方返回一些东西,或者停止并返回到main。

我正在寻找一种设计模式,这样我就可以在遇到这样的问题时使用它。基本上,当我需要退货,但没有什么可以退货时,我该怎么办?

谢谢。

您不应该在函数本身中捕获异常-异常应该用于将问题传达给调用方-他们应该知道在这种情况下什么是合理的处理。

函数必须从调用它的地方返回一些东西,或者停止并返回到main。

不完全如此——如上所述,它可以throw,并从最紧密地包围它被调用的地方的catch()块继续。

请记住,不想进行异常处理的调用者可以在决定尝试弹出之前轻松测试empty()size()。。。所以CCD_ 5将异常返回给他们是非常合理的。

此外,不要抛出字符串文字。。。请改用std::runtime_error("...")。原因在这里。

我正在寻找一种设计模式,这样我就可以在遇到这样的问题时使用它。基本上,当我需要退货,但没有什么可以退货时,我该怎么办?

如前所述,抛出异常是处理此问题的一种正常方式,尤其是对于像从空堆栈中弹出这样的情况。不过,更普遍的是,其他替代方案包括:

  • 返回哨兵值

    • 例如,如果容器存储int s,您可能能够返回0-1numeric_limits<int>::max,或者对于std::string s,可能返回一个空的,但最好的方法取决于客户端代码将容器放入的用途

    • 对于模板化容器,很难知道T的值是否可以用于此操作——例如,如果Tchar,并且二进制文件中的内容存储在容器中,则您可能想用作sentinel的任何字符值也可能出现在文件中;如果您无论如何都想尝试这种方法,通常最好要求调用方提供一个sentinel值作为模板参数或构造函数参数。

  • 返回std::optional<T>(仍在等待标准化,但可能由某些编译器提供)或boost::optional<T>

  • 如果.firsttrue,则返回std::pair<bool, T>,其中.second仅为弹出值,表示成功

  • 返回一个[smart]指针,但这很笨拙,因为您可能必须为弹出值的副本动态分配内存;智能指针有助于确保调用方delete稍后是对象

  • 只需记录调用方必须首先检查empty()size(),并且assertpop_back中确认容器不是空的-如果调用方不满足此"前提条件",则会停止程序

  • 将接口更改为bool pop_back(T& t),其中返回值表示成功,并且将设置调用者指定的T参数。

这些都不值得称为"模式"——模式往往指更复杂、更高级的设计方面。

替换当前代码

template <class T>
T Queue<T>::pop_back()
{
    try
    {
        if (empty())
            throw "The Queue is empty!";
        size--;
        back= (back- 1 + max_size_) % (max_size);
        return queue[back];
    }
    catch (char* strException)
    {
        cerr << "Error: " << strException << endl;
    }
}

像这样的东西:

template< class Type >
auto Queue<Type>::pop_back()
    -> Type
{
        if( empty() ) { throw std::runtime_error( "The Queue is empty!" ); }
        --size;
        back = (back - 1 + max_size_) % (max_size_); // Note fix of name
        return queue[back];
}

您想要将异常抛出给调用者

在函数中使用异常,仅仅跳转到输出一些文本的代码(并且未能返回函数结果)是没有意义的。


顺便说一句,在上为成员标准化一个单一的命名约定是个好主意。max_size_max_size的无意混合,很可能是一个错误,而且无论如何都是一个严重的缺陷,如果使用单一的命名约定,就不会发生这种情况。事实上,作为数据成员的backmax_size_不一致。


还要注意使用std::runtime_error而不是字符串文字作为异常对象。这支持捕获std::exception。您可能会发现其他一些标准异常类更适合,更能指示特定问题,但我通常只使用std::runtime_error

使用标准异常类是一个好主意的另一个原因是,在catch中很容易指定错误的类型。

例如,代码中的catch将不起作用:

#include <iostream>
using namespace std;
void foo()
{
    try
    {
        throw "Blah blah!";
    }
    catch( char* s )
    {
        cout << "Caught exception internally in foo(): " << s << endl;
        return;
    }
}
auto main() -> int
{
    try
    {
        foo();
    }
    catch( ... )
    {
        cout << "Failed to catch exception in foo()." << endl;
    }
}

输出:

未能在foo()中捕获异常。

确切地说,问题是您尝试了catch块。

如果抛出异常,执行流将转到catch逻辑。它将打印您的语句,并且函数将在不返回值的情况下结束。

在catch块的末尾或函数的末尾重新抛出添加return语句的异常(重新抛出似乎是最好的选择,因为您根本不处理该异常)。