使用 push_back 添加指向空 std::list 的指针的正确方法

Correct way to add a pointer to an empty std::list using push_back

提问人:Blargian 提问时间:3/8/2023 更新时间:3/8/2023 访问量:88

问:

我有两个简单的类,分别表示连接图结构中的节点和边。

//node.h

#include "edge.h"
#include <string>
#include <list>
#include <utility> 

using namespace std; 

class Edge;

class Node {
private:
    list<Edge> edgeList;
    string nodeName; 
    pair <int, int> coordinates;
public:
    Node();
    void setXY(int x, int y);
    void insertEdge(Edge& edgeToAdd);
    void removeEdge(Edge& edgeToAdd);
    list<Edge> getEdgeList();
};

在 node.cpp 中实现的方法如下:insertEdge

void Node::insertEdge(Edge& edgeToAdd) {
    this->edgeList.push_back(edgeToAdd);
}

该类是在 edge.h 中声明的另一个简单类:Edge

#pragma once
#include "node.h"

class Node; 

class Edge {
private:
    Node* destinationNode;
    int edgeWeight;
public:
    //constructor
    Edge(Node* destNode, int w);

    //Setters and Getters 
    void setDestinationNode(Node* destNode);
    void setEdgeWeight(int weight);
    Node* getDestinationNode();
    int getEdgeWeight();
};

在我编写单元测试的另一个文件中,我尝试使用上面详述的方法插入指向边缘对象的指针:insertEdge

SECTION("an edge gets inserted to the adjacency list") 
{
        Node* b = &Node();
        Edge* e = new Edge(b,1);
        b->insertEdge(*e);
        list<Edge> edgelist = b->getEdgeList();
}

我已确定由于非法内存访问而导致分段错误。通过单步执行代码,我可以看到被非法访问的变量是 std::list 的分配器的成员。我怀疑我的问题与我的声明方式有关edgeList.push_back(edgeToAdd)_Prev_Nextstd::list<Edge*> edgeList

我的问题是,将指针添加到仅声明的 std::list 的正确方法是什么?

C++ 列表 std 分配器

评论

0赞 NathanOliver 3/8/2023
Node* b = &Node();不应编译,它会获取临时对象的地址,该临时对象的地址在完整表达式结束时立即超出范围。
0赞 Blargian 3/8/2023
@NathanOliver说只有 Node* b = new Node() 是正确的用法是正确的吗?在这种情况下,内存将分配在堆上,并且不会在完整表达式结束后立即释放。
1赞 user4581301 3/8/2023
旁注:链接列表(或类似链表)非常慢,除非您在已知位置执行插入或删除。一旦你必须迭代或寻找东西,你就会输,而且与类似数组的 . 不过,确实有非常宽容的迭代器失效std::liststd::vectorlist
1赞 t.niese 3/8/2023
@Blargian在谈论对象的生存期时,您应该停止考虑堆和堆栈,而是使用存储持续时间。存储持续时间和堆栈/堆通常相关,但情况并非总是如此。stack/heap:根据实现存储数据的位置,存储持续时间:描述对象的生存期。In that case the memory would be allocated on the heap and not get released as soon as the full expression ends
2赞 Miles Budnek 3/8/2023
Edge* e = new Edge(b, 1); b->insertEdge(*e);没有理由在这里动态分配你的。您正在将其副本插入 的 ,因此您不妨只使用具有自动存储持续时间的本地。EdgeNodeedgeList

答:

1赞 user253751 3/8/2023 #1

正确的方法就是你做的方式。

但是,这一行:

Node* b = &Node();

创建一个新的,获取其地址,然后销毁该节点,因为它只是一个临时对象。然后,您访问已销毁的 .NodeNode

您可能想编写然后使用 instead of - 这会创建一个局部变量,该变量在函数结束之前保持活动状态。Node b;b.b->Node

评论

0赞 Blargian 3/8/2023
谢谢,解决了问题。我正在尝试在这里做一个图的邻接列表实现,所以我最初想到的是将指针存储在边缘列表中的节点。出于某种原因,我认为不制作副本而是使用指针或引用更有效。但是,如果 Node 对象只会在函数结束之前保持活动状态,我认为最好将 Nodes 的副本存储在边缘列表中?
0赞 user253751 3/8/2023
@Blargian要在边缘列表中存储节点的副本?(这是否包括其边缘列表的副本,包括边缘列表的副本,包括其他节点的副本的副本?永远持续下去?创建无限副本?)如果没有,你到底想做什么?