【问题标题】:What causes `std::bad_alloc` to get thrown in this code?是什么导致在这段代码中抛出 `std::bad_alloc`?
【发布时间】:2019-04-21 04:10:49
【问题描述】:

我正在尝试解决Leetcode Problem 22(这只是一个非常简单的递归应用程序),并且我试图记住结果以加快计算速度。但是,当我尝试存储指向向量的指针列表时,我遇到了std::bad_alloc 错误,而当我只是存储指针列表时,程序运行良好。我对 C++ 和指针和内存分配比较陌生,所以这可能是一个非常愚蠢的问题,但我已经看了一段时间的代码,但似乎无法弄清楚。

所以,这是代码:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Solution {
public:
    vector<string> generateParenthesis(int n) {
        if (n == 0) {
            return {};
        }
        return generateParenthesis_recursive(n);
    }

    vector<vector<string>*> memo;
    vector<string> generateParenthesis_recursive(unsigned int n) {
        if (n < memo.size()) {
            return *(memo[n]);
        }

        vector<string> result;
        if (n == 0) {
            result = {""};
        }
        else if (n == 1) {
            result = {"()"};
        } 
        else if (n > 1) {
            vector<string> left, right;
            for (unsigned int k = 0; k < n; k++) {
                left  = generateParenthesis_recursive(k);
                right = generateParenthesis_recursive(n - k - 1);
                for (auto left_parenths : left) {
                    for (auto right_parenths : right) {
                        result.push_back("(" + left_parenths + ")" + right_parenths);
                    }
                }
            }
        }

        while (memo.size() <= n) {
            memo.push_back(nullptr);
        }
        memo[n] = &result;
        return result;
    }
};

int main(int argc, char const *argv[]) {
    Solution s;

    vector<string> output = s.generateParenthesis(4);
    for (auto s : output) {
        cout << s << " ";
    }
    cout << endl;

    return 0;
}

我不太确定在哪里或为什么,但这会引发 std::bad_alloc 错误。但是,将 memo 更改为 vector&lt;vector&lt;string&gt;&gt;(并更改代码的其他各个小部分以使其有意义),它可以正常工作。

具体是什么导致了这里的错误?没有无限循环,递归有一个明确定义的基本情况,我不认为我分配了太多内存。我看不出程序如何分配内存失败。

【问题讨论】:

  • memo[n] = &amp;result; 无效,因为result 是该函数内的局部变量,这意味着它的生命周期在函数结束时结束,并且指针无效。
  • 不使用指针?首先要专注于编写有效的优秀代码。然后,如果性能不够好(通常足够足够好),则分析和测量以找到瓶颈。修复一个并再次测量以找到下一个。而且由于优化往往会使代码难以阅读和理解(因此难以维护),因此请编写 cmets 了解优化后的代码在做什么以及为什么要这样做。
  • 并通过 const& 返回您的向量,复制它们的成本很高!
  • @MatthieuBrucher ...而且移动起来相当便宜,如果 NRVO 启动,甚至免费。在这种情况下,它们不能由 & 返回,因为它们是在局部变量中计算的结果。跨度>
  • @Marc.2377 当然,继续!永远不要妨碍对正确信息的追求!

标签: c++ memory


【解决方案1】:

问题就在这里:

memo[n] = &result;

因为您引用的是局部变量。你可以把它改成这样:

memo[n] = new vector<string>(result);

但请记住,您需要手动释放这些指针。

【讨论】:

    【解决方案2】:

    正如Some programmer dude在cmets中所说,罪魁祸首就是这条线

    memo[n] = &result;
    

    因为您将一个对局部变量 (result) 的引用分配给了 memo,它在函数末尾超出了范围。


    (编辑): 下面的解决方法是错误的;不要使用它。

    这可以通过将声明vector&lt;string&gt; result; 移出函数来解决,像这样:

    vector<vector<string>*> memo;
    vector<string> result;
    vector<string> generateParenthesis_recursive(unsigned int n) {
    //...
    

    考虑使用智能指针 - 如果你真的需要指针,那就是。见:What is a smart pointer and when should I use one?

    【讨论】:

    • 强烈建议 C++ 程序员不要使用裸指针集合的全部原因是因为永远不清楚谁拥有指向的对象。这正是那个问题——您将指向对象的指针添加到返回的向量中,但没有意识到指向的对象归函数所有。 C++ 有简单的方法来保持所有权清晰(例如使用值集合、使用unique_ptr 等等)。请使用它们。
    • 你的“修复”破坏了一堆其他东西(递归调用将作用于一个不为空的 result 向量,它们所做的更改将影响外部 result 向量,并且memo 中的所有条目都将指向同一个实例)。简而言之,它根本无法解决。
    猜你喜欢
    • 2016-04-04
    • 2012-01-16
    • 2012-08-08
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    • 2013-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多