我是c ++的新手,并尝试通过实现LinkedList来熟悉该语言。
class ListElement {
public:
int val;
ListElement *next;
ListElement(int v, ListElement *n) {val = v; next = n;};
};
ListElement
包含一个int值val
和一个指向下一个列表元素的指针(如果没有下一个元素,则为nullptr
)和一个构造函数。
class MyLinkedList {
public:
ListElement *head;
MyLinkedList() {head = nullptr;};
ListElement* getHead(void){
return head;
};
void append(int i) {
head = &ListElement(i, head);
};
};
MyLinkedList
包含指向名为head
的列表的第一个元素的指针以及列表中的一些方法。遇到这些方法中的一些错误,我试图追查他们的原因。 (我知道公共类成员的getter完全没有意义,原来head
是私有的。)这样做我发现了以下无法解释的行为:
int main() {
MyLinkedList l;
l.append(1);
int x = l.head->val;
cout << "head: " << x << "\n";
int y = l.getHead()->val;
cout << "getHead(): " << y << "\n";
int z = l.head->val;
cout << "head: " << z << "\n";
cin.get();
return 0;
}
运行此代码(添加#include <iostream>
和using namespace std;
作为工作示例)打印
head: 1
getHead(): 18085840
head: -858993460
所以head
的第一次直接访问就像预期的那样工作,产生1
作为第一个列表元素的值,但使用getter返回垃圾。如果再次直接访问head
,它也会产生垃圾,这让我觉得“嗯,看起来好像使用getHead()以某种方式玷污了ListMember对象”,只是为了发现
int x = l.head->val;
cout << "head: " << x << "\n";
int z = l.head->val;
cout << "head: " << z << "\n";
打印
head: 1
head: -858993460
甚至没有碰到吸气剂。那么只是以任何方式访问l.head
只是为了惹恼它吗?
不,如
int x = l.head->val;
int z = l.head->val;
cout << "head: " << x << "\n";
cout << "head: " << z << "\n";
两次返回(按预期)head: 1
。所以在中间使用cout
更改我的对象或指针? getHead()
有什么问题,因为它只是return head;
?
所以我很迷失在这里,找不到任何直接相关的问题。 (This Question有一个很有希望的标题但不使用指针)。我是否完全没有以正确的方式使用指针?或者在幕后进行一些自动对象删除?或者这就是magiC ++的工作原理?
答案 0 :(得分:2)
在
void append(int i) {
head = &ListElement(i, head);
};
ListElement(i, head)
创建一个临时的,无名的ListElement
,并将指针指向head
。然后,由于ListElement
本身未分配给任何内容,ListElement
goes out of scope并被销毁。这使得头部指向无效的记忆。
可以编写头以使用动态内存来延长ListElement
void append(int i) {
head = new ListElement(i, head);
};
但现在有人必须负责确保在不再需要时删除ListElement
。
例如:
void remove(int i) {
// find list element i and previous element. Special handling required for first element
prev.next = element.next;
delete element;
};
谨慎使用std::unique_ptr和std::move可以实现内存管理的自动化,并消除对delete
的需求。
答案 1 :(得分:1)
变化
void append(int i) {
head = &ListElement(i, head);
};
到
void append(int i) {
head = new ListElement(i, head);
};
第一个,如果它编译,则获取临时堆栈分配对象的地址。因此head
将在它被破坏后“指向垃圾”。
答案 2 :(得分:1)
在C ++中,您必须管理自己的内存。声明
ListElement(i, head);
创建一个ListElement实例,本地作用于MyLinkedList :: append()。因此,一旦该函数退出,变量就不再存在,并且指针现在指向无效的内存。
你的第一个印刷声明给你一个看似正确的答案的原因是有点红鲱鱼。在所有情况下,您都在访问具有未定义行为的free-ed内存。在第一种情况下,内存恰好具有您先前设置的值。
你必须在追加时分配你自己的记忆,并在完成后将其清理干净。一旦掌握了&#34; new&#34;一定要查找如何遍历数据结构并删除每个元素。使用链接的列出的实现,这应该是相当简单的。