1

我有以下课程:

class Stack {
  struct Link {
    void* data;
    Link* next;
    void initialize(void* dat, Link* nxt);
  }* head;
public:
  void initialize();
  void push(void* dat);
  void* peek();
  void* pop();
  void cleanup();
};

pop方法是:

void* Stack::pop() {
  if(head == 0) return 0;
  void* result = head->data;
  Link* oldHead = head;
  head = head->next;
  delete oldHead;
  return result;
}

oldHead是一个指向 a 的指针struct Link,它有一个 void 指针作为成员。所以通过删除oldHead,我隐含地删除了那个 void 指针,对吧?

我正在阅读 Bruce Eckel 的 Thinking in C++,它说删除 void 指针并不能正确清理,因为delete需要知道指针的类型。

此代码隐式删除 void 指针data,因此:有人可以解释为什么删除 void 指针的这种(隐式)方式与删除 with 不同delete <void pointer>吗?

4

4 回答 4

2

您的术语造成歧义,但让我解释一下。假设您有:

struct foo
{
    void* bar;
};

每当 afoo结束其生命周期时,它bar也会停止存在。因此,如果您有:

{
    foo f = { new int; }
}

你已经泄露了,因为new int永远不会被删除。同样,当您这样做时:

{
    foo* f = new foo;
    f->bar = new int;
    delete f;
}

您仍然泄漏,因为何时delete f运行,您只需结束f指向的生命周期(就像上面自动发生的一样),ergobar根本不存在并且new int不会被删除。

总而言之,当对象的生命周期结束时,delete不会作为指针的成员上调用。

因此,当您在 a 上调​​用 delete 时Link,情况与上述相同barfoo您正在删除内存以Link导致adata停止存在,但实际上并未删除它所指向的内容。

于 2010-07-23T18:16:18.317 回答
0

通过删除Link,该 void* 内存空间不会被删除。您需要定义一个析构函数来删除已分配的内存。每个新的都需要一个删除。Link-struct 的一个例子是添加一个析构函数来删除data. 如果您的假设是正确的,那么next也将被删除,从而导致您的整个链接列表被删除,这将是一种可怕的行为。

对指针调用 delete 将调用指向类型的析构函数。如果该类型没有析构函数,则不会调用这样的析构函数。没有析构函数的 void 指针就是这种情况。在继承的情况下,析构函数应该始终是虚拟的,这样层次结构中最深的类就会调用它们的析构函数。即使您将指针转换为错误的类型,内存也会正确释放 - 只是析构函数会被错误地调用。

于 2010-07-23T18:14:47.003 回答
0

“我隐含地删除了那个空指针,对吧?”

好吧,当您 delete 时,您正在删除指针本身oldHead。您没有删除或释放它的target,这似乎是您想要的,也是您调用delete指针时发生的事情。

(要了解为什么会出现这种情况,请考虑您可能会定义一个带有void*指向该结构外部的指针的结构。您不希望仅仅因为该结构被删除而释放目标。)

于 2010-07-23T18:15:05.140 回答
0

当您指向带有析构函数的东西时,会发生删除 void 指针的一个问题:

#include <iostream>

struct foo
{
    ~foo() { std::cout << "important work" << std::endl; }
};

int main()
{
    foo *f = new foo;

    void *v = f;

    delete v;
}

如果您运行上面的代码示例,您将看到析构函数从未被调用。

于 2010-07-23T18:30:19.353 回答