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

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

本文关键字:访问 std 干扰 cout c++ getter 成员 通过      更新时间:2023-10-16

我是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;


所以我在这里很迷茫,找不到任何直接相关的问题。(这个问题的标题很有希望,但不使用指针)。我是不是完全没有以正确的方式使用指针?还是在幕后自动删除对象?或者这就是magiC++的工作原理?

在中

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

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

磁头可以写为使用动态存储器来延长ListElement 的寿命

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

但现在必须有人承担起确保CCD_ 23在不再需要时被删除的责任。

例如:

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的需求。

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

ListElement(i, head);

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

你的第一份书面陈述给你一个看似正确的答案的原因有点转移注意力。在所有情况下,您都在访问具有未定义行为的空闲内存。在第一种情况下,内存恰好具有您之前设置的值。

你必须在append中分配你自己的内存,并在完成后清理它。一旦你掌握了"new",一定要查找如何遍历数据结构并删除每个元素。对于链接列出的实现,这应该是相当琐碎的。

更改

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

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

第一个,如果它进行编译,就是获取一个临时堆栈分配对象的地址。因此head在销毁后会"指向垃圾"。