尝试用 C++ 编写 Graph,有时会得到 bad_alloc

Trying to code Graph in c++, getting bad_alloc some of the time

我在 Java 中学习了基本的面向对象编程后才开始接触 c++,所以我很难掌握内存释放。作业是创建一个加权有向图...

我收到错误:“在抛出 'std::bad_alloc' 的实例后调用终止 what(): std::bad_alloc" 当我 运行 通过我的代码进行某些输入时,我很难弄清楚是什么原因造成的。

我用谷歌搜索了这个错误,发现这是一个内存问题,所以我试图检查我的代码并试图找到任何泄漏,但我不确定它们在哪里。大多数帖子都在谈论指针,我不倾向于实现它们,因为我不熟悉它们。感谢您的宝贵时间!

#include <iostream>
#include <fstream>
#include <string>
#include <array>
#include <iterator>
#include <map>
#include <list>
#include <vector>
#include <algorithm>

using namespace std;

class WDGraph {
    private:
        map<string,map<string,int>> edges;
        vector<string> verts;
        list<string> leaves;
        list<string> roots;
        list<string> selfEdges;
    public:
        list<string> getRoots() { return roots; }
        list<string> getLeaves() { return leaves; }

        void addVert(string key) {
            verts.push_back(key);
        }

        void link(string start, string dest, int cost) {
            edges[start].insert(make_pair(dest,cost));
            if (!containsLeaf(dest) && !containsVert(dest)) 
                leaves.push_back(dest);
            if (!containsRoot(start) && !containsVert(start)) 
                roots.push_back(start);
            if (start == dest)
                    selfEdges.push_back(start);
            roots.remove(dest);
            leaves.remove(start);
        }

        bool containsVert(string key) {
            for (int i=0; i < verts.size(); i++) {
                if (key == verts[i]) {
                    return true;
                }
            }
            return false;
        }

        bool containsRoot(string key) {
            bool found = (find(roots.begin(), roots.end(), key) != roots.end());
            return found;
        }

        bool containsLeaf(string key) {
            bool found = (find(leaves.begin(), leaves.end(), key) != leaves.end());
            return found;
        }

        WDGraph() { }
        void printWDG() { 
            cout << "Printing Weighted Directed Graph." << endl;
            for (auto itr1 = edges.begin(); itr1 != edges.end(); ++itr1) {
                for (auto itr2 = itr1->second.begin(); itr2 != itr1->second.end(); ++itr2) {
                    if (itr2->first == "null" && containsRoot(itr1->first)) {
                        cout << "[" << itr1->first << "]";
                    }
                    else if (itr2->first != "null")
                        cout << "[" << itr1->first << " -> ";
                        cout << itr2->first << ", " << itr2->second << "] ";
                } 
                cout << "" << endl;
            }            
        }

        void printNumVerts() { 
            cout << "Total number of vertices: " << verts.size() << endl;
        }

        void printRoots() { 
            int num_roots = 0;
            cout << "Vertices with zero inbound edges: " << endl;
            for (auto itr = roots.begin(); itr != roots.end(); ++itr) { 
                cout << "[" << *itr << "]" << endl;
                num_roots++;
            }
            if (num_roots == 0) cout << "None" << endl;
        }

        void printLeaves() { 
            int num_leaves = 0;
            cout << "Vertices with zero outbound edges:" << endl;
            for (auto itr = leaves.begin(); itr != leaves.end(); ++itr) {
                if (*itr != "null")
                    cout << "[" << *itr << "]" << endl;
                    num_leaves++;
            }
            if (num_leaves == 0) cout << "None" << endl;
        }

        void printSelfEdges() {
            cout << "Vertices with self edges:" << endl;
            for (auto itr = selfEdges.begin(); itr != selfEdges.end(); ++itr) {
                cout << "[" << *itr << "]" << endl;
            }
        }    
};

int main() {
    WDGraph myWDG;
    string filePath;
    string line;
    int weight;
    size_t commaPos;
    vector<string> sVector;
    ifstream dataFile;

    // cout << "Please enter the relative path to an input file." << endl;
    // getline (cin, filePath);
    // cout << "The file path you entered was " << filePath << endl;
    // dataFile.open(filePath);

    dataFile.open("input.csv"); //test input

    while (getline (dataFile, line)) {
        commaPos = line.find(',');

        //Parse input file into string vector
        while (line.length() >= 1) { 
            if (line.length() == 1) {
                sVector.push_back(line);
                break;
            }

            sVector.push_back(line.substr(0,commaPos));
            line = line.substr(commaPos+1);
            commaPos = line.find(',');   
        }

        //Create vertices depending on number of parameters
        if (sVector.size() == 1) {            
            if (!myWDG.containsVert(sVector[0])) {
                myWDG.addVert(sVector[0]);\
            } 
            myWDG.link(sVector[0], "null", 0);
        }

        if (sVector.size() == 3) {
            if (!myWDG.containsVert(sVector[0])) {
                myWDG.addVert(sVector[0]);
            } 
            if (!myWDG.containsVert(sVector[1])) {
                myWDG.addVert(sVector[1]);
            } 

            weight = stoi(sVector[2]);
            myWDG.link(sVector[0], sVector[1], weight);
        }
        sVector.clear();
    }

    myWDG.printWDG();
    myWDG.printNumVerts();
    myWDG.printRoots();
    myWDG.printLeaves();
    myWDG.printSelfEdges();
}

当我的 .csv 包含简单内容时,它会按预期工作,例如:

a,b,1
c,d,2
e
f,f,3

但是,如果我有这样的东西,我会收到错误消息“在抛出 'std::bad_alloc' 的实例后调用终止:

Hello
World,Hello,3
My,Name,4
Is
Nikki,Hello,3

欢迎来到 Stack Overflow。 注意:对这种风格感到抱歉,但你真的必须学会自己解决这类问题。这叫做调试。我是一位经验丰富的程序员,但我的代码从未 运行 完全像我第一次测试时所想的那样。您需要学习如何使用 gdb 之类的调试器或 Visual C++ 环境中的内置调试器。

现在关于你的问题: 以下代码接收了值为 Hello 的变量 lineline 中没有 , 字符,因此 line = line.substr(commaPos + 1); return Hello 始终存在,并且由于 'Hello' 字符串包含一个以上的字符,您陷入无限循环。

//Parse input file into string vector
        while (line.length() >= 1) { 
            if (line.length() == 1) {
                sVector.push_back(line);
                break;
            }   
            sVector.push_back(line.substr(0, commaPos));
            line = line.substr(commaPos + 1);
            commaPos = line.find(',');
        }

问题不止于此。由于无限循环的每次迭代你的程序执行:sVector.push_back(line.substr(0, commaPos)); 你实际上分配了越来越多的内存,直到你的系统不再给这个过程。这就是您获得 bad_alloc 异常的地方。 换句话说,你的错误不是关于C++,而是关于糟糕的编程。

重新考虑您的程序,并考虑您希望如何像 Hello 一样处理 edge-cases。 哦,永远不要在堆栈上构建对象。我知道有些地方声称可以在 main 函数中执行此操作,但相信我这会造成很多麻烦。

如 Z E Nir ​​所述,如果行中没有逗号“,”,您的行解析代码将无法使用任何输入。您当然可以调试您的行解析代码,因为无论如何调试都是一项宝贵的开发技能。

但是,调试的一种可能替代方法是找到一个现有的 C++ 语言构造,它可以执行您想要执行的操作,并且是语言库的一部分,因此它已经过调试。

很多时候,您想做的是“常见的东西”,因此调试手动代码比找到合适的 pre-existing 语言结构要花费更多时间,礼貌你最喜欢的互联网搜索引擎 and/or Whosebug 本身。而能够快速找到语言结构也是一项非常宝贵的技能。

在你的例子中,函数 getline() 接受一个可选的分隔符,默认情况下它是一个换行符,但你可以改为使用“,”作为分隔符,因此使用 getline( ) 再次,但解析单行。它只需要一个字符串对象伪装成一个文件流,即一个 std::istringstream 对象。

所以你最终得到两个嵌套循环,都使用 getline():

    #include  <sstream>

    while (getline (dataFile, line)) {

        std::istringstream  iss{line};
        std::string         token;

        while (getline (iss, token, ',')) {
            std::cout << "DEBUG  TOKEN  LEN=" << token.length() << std::endl;
            sVector.push_back(token);
        }

        // go build myWDG
    }

这样一来,您就不必搞砸诸如 commaPos 变量值之类的低级细节。并且生成的代码对于其他程序员来说更容易理解。