Delete 无法识别 List 中的指针分配

Posted

技术标签:

【中文标题】Delete 无法识别 List 中的指针分配【英文标题】:Delete does not recognise pointer allocation from List 【发布时间】:2016-04-18 05:53:06 【问题描述】:

当我尝试对指向 struct Vertex 的指针调用 delete(使用 Vertex * v = new Vertex 分配,然后在我的类析构函数中成功使用并存储在 std::list 中时,我收到此运行时错误:

graphtake3(12325,0x100082000) malloc: *** error for object 0x100200340: pointer being freed was not allocated
***

指针肯定是被分配的,因为应用程序运行良好,并且一切都在堆栈跟踪中显示,但由于某种原因,delete 似乎无法释放它们。这里发生了什么,为什么不删除工作?

以下是相关的缩写代码:

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

enum Color BLACK, GREY, WHITE;

struct Vertex 
    int id;
    std::string name;
    Color color;

    Vertex();
    Vertex(std::string name);

    ~Vertex();

;
class Graph 
    std::vector<std::list<Vertex *>> adjList;

public:
    Graph();
    Graph (int nodeCount);

    ~Graph();

    int newVertex();
    int newVertex(std::string name);
    void newUnDirectedEdge(int v1, int v2);
    void newDirectedEdge(int v1, int v2);
    std::list<Vertex*> getConnections(int v);

    friend std::ostream& operator<<(std::ostream& os, const Graph& g);



;

#include "Graph.hpp"

Vertex::Vertex() 
    color = WHITE;


Vertex::Vertex(std::string name) 
    this->name = name;
    color = WHITE;


Vertex::~Vertex() 



Graph::Graph() 



Graph::Graph(int nodeCount) 
    adjList.reserve(nodeCount);


Graph::~Graph()
    for (int i = 0; i<adjList.size(); i++) 

        for (std::list<Vertex*>::iterator iterator = adjList[i].begin(), end = adjList[i].end(); iterator !=end; iterator++) 
            delete (*iterator); //fails
        
    


int Graph::newVertex() 
    Vertex * v = new Vertex();
    adjList.push_back(std::list<Vertex *>(1, v));
    v->id= (int)adjList.size()-1;
    return v->id;


int Graph::newVertex(std::string name) 
    Vertex * v = new Vertex();
    adjList.push_back(std::list<Vertex *>(1, v));
    v->id= (int)adjList.size()-1;
    v->name= name;
    return v->id;


void Graph::newUnDirectedEdge(int v1, int v2) 
    newDirectedEdge(v1, v2);
    newDirectedEdge(v2, v1);


void Graph::newDirectedEdge(int v1, int v2) 
    Vertex * vertex2 = adjList[v2].front();
    adjList[v1].push_back(vertex2);


std::list<Vertex*> Graph::getConnections(int v) 
    return adjList[v];


std::ostream& operator<<(std::ostream& os, const Graph& g) 
    for (int i = 0; i<g.adjList.size(); i++) 
        for (std::list<Vertex*>::const_iterator iterator = g.adjList[i].begin(), end = g.adjList[i].end(); iterator !=end; iterator++) 
            os << (*iterator)->id << " (" << (*iterator)->name << ") ";
        
        os << '\n';
    

    return os;

与主:

#include <iostream>
#include "Graph.hpp"


int main(int argc, const char * argv[]) 
    Graph graph(5);

    int v1 = graph.newVertex("Paris");
    int v2 = graph.newVertex("London");
    int v3 = graph.newVertex("Lyon");
    int v4 = graph.newVertex("Nice");
    int v5 = graph.newVertex("Marseille");
    int v6 = graph.newVertex("La Rochelle");
    int v7 = graph.newVertex("Toulon");

    graph.newUnDirectedEdge(v2, v1);
    graph.newUnDirectedEdge(v1, v3);
    graph.newUnDirectedEdge(v1, v4);
    graph.newUnDirectedEdge(v3, v4);
    graph.newUnDirectedEdge(v5, v4);
    graph.newUnDirectedEdge(v7, v5);

    std::cout << graph;


    return 0;

【问题讨论】:

请发布一个最小但完整的演示,读者可以尝试。这听起来像是一个三规则问题。但没有代码就无法确定。 @Cheersandhth.-Alf 好的,我添加了一个更完整的实现 感谢您的更新,顺便说一句。将 Graph(const Graph&amp;) = delete;(假设您使用 C++11)添加到您的 Graph 类声明中。如果您的代码编译开始在各个地方呕吐,那么您肯定违反了rule-of-three。 @WhozCraig 好的,我添加了我的main()。我还没有实现复制构造函数,所以我是,但是在这种情况下如何阻止删除工作? @TheInnerParty 我刚刚运行了你的代码,在这种情况下它不会因为Graph 从未被复制(想想如果它会发生什么,两个Graph 对象有两个@987654335 @ 都包含 same 指针)。顺便说一句,这与您的实际问题密切相关。您的邻接列表可以共享​​>指向相同顶点对象的指针。至少从我所看到的来看,他们似乎是这样做的。清除列表后,每个 other 列表都包含指向刚刚清除的列表中的一个或多个顶点的指针现在悬空,并且使用它们会调用未定义的行为。 【参考方案1】:

你这样做的那一刻,你就为灾难做好了准备:

void Graph::newDirectedEdge(int v1, int v2) 
    Vertex * vertex2 = adjList[v2].front();
    adjList[v1].push_back(vertex2);

问题是你没有区分谁拥有一个指针。在这种情况下,您只需将指针复制到列表中。但是当您转到~Graph 时,您会删除列表中的所有 指针。有的是new获取的,有的是上面函数复制的。

所以错误是正确的:指针未分配。发生的事情是您已经删除了它,然后您删除了它的副本。

您需要重新考虑您的设计并考虑指针所有权。或者您可以使用大锤方法将所有内容转换为std::shared_ptr。但我实际上并不建议这样做。

图的一种常见方法是存储所有顶点(在std::vector 中),然后像您所做的那样连接到一个单独的结构中。然后在析构函数中,您只需撕开向量。您甚至可以借此机会学习如何使用std::unique_ptr。 =)

【讨论】:

你说得对,我没有意识到这一点。这被认为是一个好的解决方案:删除时,检查指针是否为空,否则调用delete并将指针设置为空? 不,delete 对空指针没有影响。问题是您有两个指针副本,并且您尝试将它们都删除。删除一个并不会神奇地将另一个的值更改为 null。 将指针设置为 null 不会使您免于此错误。复制的指针仍然保持不变,删除仍然会失败。 好的,我想我明白了:只需删除每个列表中的第一个指针,如下所示:for (int i = 0; i&lt;adjList.size(); i++) delete (adjList[i].front()); 现在它们都将是唯一的,它将通过每个顶点 新编辑的答案实际上似乎是一个更好的方法,我会这样做:)【参考方案2】:

问题是否出现在 newDirectedEdge 函数中? 在它的实现中,它复制指针并再次插入列表,然后将两个指针指向列表中的一个地址,第一次删除是可以的,第二次......崩溃......

【讨论】:

以上是关于Delete 无法识别 List 中的指针分配的主要内容,如果未能解决你的问题,请参考以下文章

无法分配给 C++ 中的指针数组

角色分配策略中无法识别 aws 自定义属性

d 对象的动态建立和释放

在 cpp 中重新定义 new 和 delete 运算符以跟踪内存分配:无法跟踪整个内存块的删除大小

C++:delete[] 错误,指针未分配

用户分配的角色仍然无法识别