【问题标题】:c++ class member accessed via getter yields garbage, direct access ok but std::cout interferes?通过 getter 访问的 c++ 类成员会产生垃圾,直接访问可以,但 std::cout 会干扰吗?
【发布时间】:2016-07-21 22:09:05
【问题描述】:

我是 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 &lt;iostream&gt;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 两次。那么在更改我的对象或它们的指针之间使用coutgetHead() 有什么问题,因为它只是 return head;


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

【问题讨论】:

  • head = &amp;ListElement(i, head); - 这不是您分配新ListElement 的方式。去阅读new(然后在大多数情况下,尽量避免需要它)。
  • 为了更好的衡量标准,您可能还想了解smart pointers
  • @jaggedSpire 是的,我的错 - 我猜想得太迟了:\

标签: c++ pointers getter cout


【解决方案1】:

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_ptrstd::move 可以实现内存管理的自动化并消除对delete 的需要。

【讨论】:

    【解决方案2】:

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

    ListElement(i, head);
    

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

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

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

    【讨论】:

      【解决方案3】:

      改变

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

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

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

      【讨论】:

        猜你喜欢
        • 2021-05-04
        • 1970-01-01
        • 2010-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-07-20
        • 2013-12-27
        相关资源
        最近更新 更多