【问题标题】:Fix a return issue.Warning: control reaches end of non-void function in C++修复返回问题。警告:控制到达 C++ 中非 void 函数的末尾
【发布时间】:2015-04-16 11:42:44
【问题描述】:

我知道这可能看起来像重复,但其他问题没有我正在寻找的答案。

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 *'的实例后调用终止 中止(核心转储)”

有哪些提示和建议可以解决此问题?我在那里有一个 try catch 块,但它没有多大帮助。函数必须从调用它的地方返回一些东西,或者停止并返回到 main。

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

谢谢。

【问题讨论】:

  • 如果您对异常很认真,请创建一些类而不是抛出指向字符串文字的指针。如果您不处理异常,甚至不必费心去捕捉它。
  • 为此抛出异常是非常值得怀疑的。真的很特别吗?你可以在 if 语句中做“错误:”消息的事情,不要增加不必要的开销。考虑在容器中没有剩余元素的情况下返回空指针。

标签: c++ exception exception-handling return


【解决方案1】:

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

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

不完全是 - 如上所述,它可以throw 并从最接近调用它的位置的catch() 块继续。

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

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

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

如前所述,抛出异常是处理这个问题的一种正常方式,尤其是对于像从空堆栈中弹出这样的事情。更一般地说,其他替代方案包括:

  • 返回一个标记值

      1234563最好取决于客户端代码放置容器的用途
  • 1234563您可能想用作标记的任何字符值也可能出现在文件中;如果你还是想试试这个,通常最好让调用者提供一个标记值作为模板参数或构造函数参数。
  • 返回std::optional&lt;T&gt;(仍有待标准化,但可能由某些编译器提供)或boost::optional&lt;T&gt;

  • 返回一个std::pair&lt;bool, T&gt;,其中.second只是.firsttrue时弹出的值,表示成功

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

  • 只需记录调用者必须首先检查empty()size(),并且asserting 表明容器在pop_back 中不是空的 - 如果此“前提条件”不存在,这将停止程序来电者遇到了

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

  • 这些都不值得被称为“模式”——模式往往指的是更复杂的高级设计方面。

    【讨论】:

    • 你建议在 main 或 driver 中试一下?
    • @user2512806:是的,把它放在那里而不是在pop_back里面。
    • 这是个好主意。问题是我不会是编写驱动程序的人。其他人会编写它,他们可能有也可能没有异常处理。这就是为什么我要在这里处理它。
    • @user2512806 好吧,客户端代码应该测试记录在案的 API - 如果他们没有捕捉到您的异常,则应将其视为错误。如果您出于某种原因不得不迎合他们,请考虑我的答案底部列出的其他选项之一......例如哨兵值或optional&lt;T&gt;。我添加了bool pop_back(T&amp; t) 作为另一种选择....
    【解决方案2】:

    替换当前代码

    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 的无意混合,这很可能是一个错误,无论如何是一个严重的缺陷,不会发生在单一的命名约定中。事实上,back 作为数据成员与max_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() 中捕获异常。

    【讨论】:

    • 为什么这不起作用?根据本教程,它应该在第一次看到异常时捕获异常。 link
    • const char[N] 类型的文字字符串。用作throw 的参数,它衰减为const char*char* 无法捕获该类型,就像您无法将 const char* 传递给 char* 正式函数参数一样。
    【解决方案3】:

    问题恰恰是你有 try-catch 块。

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

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

    【讨论】:

    • 好的,如果我添加一个返回语句,那会是什么?我不能返回任何我想要的东西。作为一个模板,它会根据类型变得更加困难。
    • 你在其他cmets中提到的教程是错误的。您可以在可以对它们做任何事情的级别上捕获异常。您的代码是一个很好的例子;如果您捕获并隐藏异常,您将强制您的函数返回一个本质上错误的值。调用您的函数的客户端代码将知道异常是否可以被忽略或者是其他地方编程失败的信号。
    • 我不知道这是错的,感谢您告诉我。所以,基本上你是说我应该抛出一个异常,但我不应该试图捕捉它。客户端(驱动程序,主程序)应捕获异常并相应地处理它。这就是你想要暗示的吗?
    猜你喜欢
    • 2012-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-22
    • 2013-04-29
    • 2022-12-17
    • 2018-08-02
    相关资源
    最近更新 更多