C ++ 11 использует умные указатели, но получает неверный указатель - PullRequest
0 голосов
/ 08 января 2019

Я использую unique_ptr и shared_ptr в этом назначении C ++, код работает до конца, пока не появится

free(): invalid pointer
Aborted (core dumped)

Я не могу понять, как это происходит.

Код анализирует файл "resource.txt" и создает график. Каждое слово является узлом, и каждая строка подразумевает зависимость между двумя словами. resource.txt

handgun bullets
bullets ore
bombs ore
turret bullets

Ниже мой вывод и код:

вывод ./main

adding edge: 'handgun' --> 'bullets'
adding edge: 'bullets' --> 'ore'
adding edge: 'bombs' --> 'ore'
adding edge: 'turret' --> 'bullets'
graph now has nodes:
bombs-->
        ore
ore-->
turret-->
        bullets
handgun-->
        bullets
bullets-->
        ore
=======
graph destructor
destructor of node: bombs
destructor of node: turret
destructor of node: handgun
destructor of node: bullets
destructor of node: turret
free(): invalid pointer
Aborted (core dumped)

Вы можете видеть, что каждый деструктор был вызван, и я использую умные указатели, но я все еще получил ошибку неверного указателя.

main.cpp

#include "graph.h"
#include <memory>

int main(int argc, char const *argv[])
{
    unique_ptr<graph> g(new graph("./resource.txt"));
    g->dispay();
    cout << "=======" << endl;
    return 0;
}

graph.h

#ifndef Tan_Li_Graph_Head_
#define Tan_Li_Graph_Head_

#include "node.h"
#include <unordered_map>
#include <memory>
#include <iostream>
#include <string>
using namespace std;

class graph
{
  private:
    /* data */
  public:
    unordered_map<string, shared_ptr<node>> nodes;
    graph(string resourceFileName);
    bool addEdge(string from, string to);
    void delNode(string name);
    void dispay();
    ~graph();
};

#endif

graph.cpp

#include "graph.h"
#include <fstream>
#include <utility>
using namespace std;

graph::graph(string fileName)
{
    ifstream file(fileName);
    string str;
    while (getline(file, str))
    {

        if (str.back() == '\r')
        {
            str.pop_back();
        }

        auto space_loc = str.find(" ");
        string from = str.substr(0, space_loc);
        string to = str.substr(space_loc + 1);
        this->addEdge(from, to);
    }
}

bool graph::addEdge(string from, string to)
{
    cout << "adding edge: '" << from << "' --> '" << to << "\'" << endl;

    if (this->nodes.count(from) == 0)
    {
        shared_ptr<node> node_from = make_shared<node>(from);
        this->nodes.insert(make_pair(from, node_from));
    }

    if (this->nodes.count(to) == 0)
    {
        shared_ptr<node> node_to = make_shared<node>(to);
        this->nodes.insert(make_pair(to, node_to));
    }

    return this->nodes[from]->addChild(this->nodes[to]);
}

void graph::delNode(string name)
{
    auto node_to_del = this->nodes[name];

    for (auto parent : node_to_del->parents)
    {
        parent.second->delChild(node_to_del);
    }

    for (auto child : node_to_del->children)
    {
        node_to_del->delChild(child.second);
    }
}

void graph::dispay()
{
    cout << "graph now has nodes: " << endl;
    for (auto node_to_display : this->nodes)
    {
        // cout << node_to_display.second->name << ": " << (node_to_display.second->useable ? "usable" : "NOT usable")
        //      << " ;" << endl;
        cout << node_to_display.second->name << "-->" << endl;
        for (auto child : node_to_display.second->children)
        {
            cout << '\t' << child.second->name << endl;
        }
    }
}

graph::~graph()
{
    cout << "graph destructor" << endl;
}

node.h

#ifndef Tan_Li_Node_Head_
#define Tan_Li_Node_Head_
#include <string>
#include <unordered_map>
#include <memory>
#include <iostream>
using namespace std;

class node
{
  private:
    /* data */
  public:
    string name;
    bool useable;
    unordered_map<string, shared_ptr<node>> children;
    unordered_map<string, shared_ptr<node>> parents;
    node(string name);
    bool addChild(shared_ptr<node> child);
    bool addChild(string name);
    bool delChild(shared_ptr<node> child);
    bool delChild(string name);
    ~node();
};

#endif

node.cpp

#include "node.h"
#include <utility>
using namespace std;

node::node(string name)
{
    this->name = name;
    this->useable = true;
}

bool node::addChild(shared_ptr<node> child)
{
    string child_name = child->name;

    if (child_name.compare(this->name) == 0)
    {
        cout << "Node " << this->name << " can't add itself to children" << endl;
        return false;
    }

    if (this->children.count(child_name) == 0)
    {
        this->children.insert(make_pair(child_name, child));
        child->parents.insert(make_pair(this->name, this));
    }
    else
    {
        cout << "Node " << child_name << " is already in the child of node " << this->name << endl;
        return false;
    }

    return true;
}

bool node::addChild(string name)
{
    if (name.compare(this->name) == 0)
    {
        cout << "Node " << this->name << " can't add itself to children" << endl;
        return false;
    }

    shared_ptr<node> child = make_shared<node>(name);
    this->addChild(child);
}

bool node::delChild(shared_ptr<node> child)
{
    string child_name = child->name;

    if (this->children.count(child_name) == 0)
    {
        cout << "Node " << child_name << " is NOT the child of node " << this->name << endl;
        return false;
    }
    else
    {
        child->parents.erase(this->name);
        this->children.erase(child_name);
    }

    this->useable = false;

    return true;
}

bool node::delChild(string name)
{
    if (this->children.count(name) == 0)
    {

        cout << "Node " << name << " is NOT the child of node " << this->name << endl;
        return false;
    }
    else
    {
        this->children[name]->parents.erase(this->name);
        this->children.erase(name);
    }

    this->useable = false;

    return true;
}

node::~node()
{
    cout << "destructor of node: " << this->name << endl;
}

1 Ответ

0 голосов
/ 08 января 2019

Скорее всего, проблема в этой строке:

    child->parents.insert(make_pair(this->name, this));

То, что вы делаете, это неявное создание указателя shared_ptr из this.

См. это для более мелкого воспроизведения вашей проблемы.

shared_ptr - это нерушимая клятва. Как только указатель управляется shared_ptr, его жизненный цикл не может контролироваться никаким другим классом / оболочкой. Память для указателя this уже создана и, следовательно, управляется другим экземпляром shared_ptr. Создав (неявно) другой экземпляр shared_ptr, используя this, вы нарушаете контракт.

Есть несколько способов решить проблему. Один из способов - сохранить необработанные указатели для любых задников (например, необработанный указатель для родителей). Таким образом, родитель контролирует цикл ребенка. Другой способ - использовать

unordered_map<string, weak_ptr<node>> parents;

Вам нужно будет извлечь node из enable_shared_from_this и затем сделать:

    child->parents.insert(make_pair(this->name, weak_from_this()));

weak_from_this

Обратите внимание, что вы можете продолжать использовать shared_ptr как для родителя, так и для ребенка, но тогда это будет циклическая ссылка , и у вас будет утечка памяти.

...