通过getter访问的c ++类成员产生垃圾,直接访问ok但是std :: cout干扰了吗?

时间:2016-07-21 22:09:05

标签: c++ pointers getter cout

我是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 ++的工作原理?

3 个答案:

答案 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_ptrstd::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;一定要查找如何遍历数据结构并删除每个元素。使用链接的列出的实现,这应该是相当简单的。