通过 getter 访问的 c++ class 成员产生垃圾,直接访问可以,但 std::cout 会干扰?

c++ class member accessed via getter yields garbage, direct access ok but std::cout interferes?

我是 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 的列表的第一个元素的指针,以及一些在列表上工作的方法。在这些方法中遇到一些错误,我试图找出它们的原因。 (我知道 public class 成员的 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 returns 垃圾。如果再直接访问head,也会产生垃圾,让我想到了"Hm, seems like using getHead() somehow garbles the ListMember object",才发现

int x = l.head->val;
cout << "head: " << x << "\n";
int z = l.head->val;
cout << "head: " << z << "\n"; 

打印

head: 1
head: -858993460

甚至没有触及 getter。那么简单地以任何方式访问 l.head 就足以混淆它了吗?

不,因为

int x = l.head->val;
int z = l.head->val;
cout << "head: " << x << "\n";
cout << "head: " << z << "\n"; 

returns(如预期)head: 1 两次。因此,在两者之间使用 cout 会更改我的 object 或它们的指针吗? getHead() 有什么问题,因为它所做的只是 return head;


所以我在这里很迷茫,找不到任何直接相关的问题。 (This Question 有一个很有前途的标题,但不使用指针)。我是否完全没有以正确的方式使用指针?还是在幕后进行了一些自动 object 删除?或者这就是 magiC++ 的工作原理?

改变

void append(int i) {
    head = &ListElement(i, head);
};

void append(int i) {
    head = new ListElement(i, head);
};

第一个,如果它编译,是获取临时堆栈分配对象的地址。因此 head 销毁后会 "point at garbage"。

在 C++ 中,您必须管理自己的内存。声明

ListElement(i, head);

在 MyLinkedList::append() 的本地范围内创建一个 ListElement 实例。因此,一旦该函数退出,变量将不再存在,指针现在指向无效内存。

你的第一个打印语句给你一个看似正确的答案的原因有点转移注意力。在所有情况下,您都在访问具有未定义行为的已释放内存。在第一种情况下,内存恰好具有您之前设置的值。

你必须在追加中分配你自己的内存,用完后清理它。掌握 "new" 后,一定要查看如何遍历数据结构并删除每个元素。对于您的链接列表实现,这应该是相当微不足道的。

void append(int i) {
    head = &ListElement(i, head);
};

ListElement(i, head) 创建一个临时的、无名的 ListElement 并将指向它的指针分配给 head。然后,因为 ListElement 本身没有分配给任何东西,所以 ListElement goes out of scope 被销毁了。这使 head 指向无效内存。

可以写入磁头以使用动态内存来延长 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 and std::move 可以实现内存管理的自动化并消除 delete.

的需要