为什么我的类节点覆盖自身而不是创建新的节点对象



有人可以运行此代码并告诉我为什么插入中的节点不断覆盖吗?

#ifndef LinkedList_hpp
#define LinkedList_hpp
#include <stdio.h>
#include <utility>
template<class T>class LinkedList{
public:
LinkedList(){
head = nullptr;
tail = nullptr;
size = 0;
}
//void insert(T val);
class Node{
public:
Node* next;
T* value;
Node* prev;
Node(T* value){
this->value = value;
}
Node(){
}
Node(T* value,Node* prev, Node* next){
this->value = value;
this->next = next;
this->prev = prev;
}
Node* operator=(const Node& node){
this->value = node.value;
this->prev = node.prev;
this->next = node.next;
return *this;
}
};
public:
Node* head;
Node* tail;
int size;
void insert(T val){

在此行中,如果前一个 head 为 10,则当前 val 40 将覆盖旧的 head 值并插入一个 val 为 40 的新节点

Node* temp = new Node(&val);
if(head==nullptr){
head = temp;
tail = temp;
}else{
temp->next = head;
head->prev = temp;
head = temp;
}
size++;
}
#endif
#include <iostream>
#include "LinkedList.hpp"
int main(int argc, const char * argv[]) {
// LinkedList<int> t;
int h = 7;
int j = 10;
int k = 40;
LinkedList<int>* list1 = new LinkedList<int>();

list1->insert(h);
list1->insert(j);
list1->insert(k);
return 0;
}

每次调用 insert 并构造一个新节点时,它都会覆盖旧值,并且所有内容都变为当前 Val

void insert(T val){

val是此函数的参数。这个对象,这个val,只存在到这个函数返回。此时它被销毁,就像在函数内部的非静态作用域中声明的其他所有内容一样。这就是C++的工作方式。一旦insert()回来,这val就不再了。它不复存在。它去见它的制造者。它变成了一个前对象,不再存在,完全是过去。

您的insert()函数执行以下操作:

Node* temp = new Node(&val);

您将指向此val参数的指针传递给Node的构造函数,然后Node将指向参数的指针保存到insert(),作为它自己的类成员。

这很好,但是一旦insert()返回,new-edNode中保存的指针就会变成指向已销毁对象的指针,取消引用此指针将成为未定义的行为。

然后,您稍后尝试取消引用不再指向有效对象的原始指针。

这解释了代码中观察到的未定义行为。

最重要的是,您的类和模板的设计存在根本缺陷。Node使用指针没有明显的目的。Node应该简单地将T存储为自己的类成员,作为value,而不是value指向存在于某处的其他T的指针,并且可以随时被销毁,这不受Node的控制。

所示代码中的另一个问题是Node的两个构造函数无法初始化nextprev指向NULL的指针。这也将导致未定义的行为。

void insert(T val)

按值获取其参数,因此val是本地副本而不是原始副本。

Node* temp = new Node(&val);

存储指向此本地副本的指针。副本超出范围,因此插入退出后您看到的是内存中存在的不再有效的幽灵。在这种情况下,重影似乎始终持有最后一个值集。

溶液:

智能方式:直接存储Node::value,而不是作为需要与节点一起保持活动状态的指针。这种方式的内存管理要少得多。

T* value;

成为

T value;

Node(T* value){
this->value = value;
}

成为

Node(T value){
this->value = value;
}

value的其他用途必须相应地更新。一般来说,new非常令人头疼,应该谨慎使用。

愚蠢的方式:通过引用传递

void insert(T &val)

以便指针指向寿命更长的原始文件。

最新更新