【问题标题】:Deleting object vs setting pointer to null删除对象与将指针设置为空
【发布时间】:2018-10-02 23:11:08
【问题描述】:

我不明白删除对象和将指针设置为 null 的工作原理。

例如:

Class* c = new Class();

我们可以将指针设置为空

c=null;

在这种情况下,对象仍在内存中,没有任何东西指向它。我们要如何删除它?

delete c;

在这种情况下,我们删除了一个对象,但指针仍然指向前一个对象的地址位置。在哪些情况下可能有用?

【问题讨论】:

  • 这就是为什么你必须 delete c 然后将 c 设置为 null...
  • 如果您没有delete 对象,并且不再有任何指向它的指针或引用,那么您不能 删除它。这就是所谓的泄漏。不要那样做。 delete 破坏对象并释放与之相关的内存,这就是它指定要做的所有事情。
  • 如果我们只是设置为null,那是最糟糕的编程习惯吗?因为对象会在内存中的某个地方徘徊而几乎无法访问?
  • 如果你应该将对象的销毁和指向它的指针设置配对,这取决于你的代码、你的用例和你的设计。在大多数情况下,它是不需要的,分配是无用的操作。
  • 应该做的始终是将每个newdelete 配对,并将每个new[]delete[] 配对。然而,现在对简单指针的需求变得越来越少,除了多态性(虚拟函数、抽象接口类等)。对于类似资源的对象,使用std::unique_ptrstd::shared_ptr。对于动态数组,请使用std::vector

标签: c++


【解决方案1】:

在将指针设置为nullptr 之前,您应该先delete。但如果您不再使用它,则无需将其设置为nullptr。在这种情况下,让指针悬空(指的是无效的内存块)是可以的。

但我总是更喜欢RAII,它说在构造函数中分配资源并在析构函数中释放它。它减少了指针的使用。那么您不需要使用指针来创建对象实例。当然,这并不总是实用的,但这是一个很好的做法。

// RAII implementation

class ClassA
{
    char *buffer;
public:
    ClassA(): buffer(new buffer[1000])
    {
    }

    ~ClassA()
    {
        delete buffer;
        // now *buffer is invalid (dangling pointer) but it is easy to
        // rely that this invalid code is not called anymore.
    }
}

...

int main()
{
   ClassA instance;
   // here you do not need to use a pointer.
}

【讨论】:

  • 我相信这与 RAII 无关。这只是对dangling 指针的混淆。
【解决方案2】:

要释放由new 分配的对象,您应该使用delete

要防止指针为"dangling",请将指针设置为nullptr

Class* c = new Class();
delete c;    // free up the allocated memory from new
c = nullptr; // prevent dangling pointer

如果c在销毁后不再使用,则不需要将其分配给nullptr

delete 之后将指针设置为nullptr 的一个例子是因为空守卫,例如:

if (c != nullptr) {
   delete c;
   c = nullptr;
}

这是冗余,因为delete 已经检查指针是否仍指向有效的内存位置。

如果它已经是nullptr,那么它什么也不做。

来自expr.delete#7.3

否则,删除表达式不会调用释放 函数。

[ 注意:无论是否调用释放函数 无论是对象的析构函数还是数组的某个元素 抛出异常。 — 尾注 ] 如果操作数的值 delete-expression 是一个 空指针 值,它是 unspecified 是否 释放函数将按上述方式调用。

【讨论】:

  • 你的意思是nullptr而不是null
  • @MM,是的。谢谢!修改。
【解决方案3】:

[设置 c=null] 对象仍然在内存中,没有任何东西指向它。如何 我们要删除它吗?

您不能——在这种情况下,您的程序有一种称为memory leak 的错误,因为如果没有指向对象的指针,您就无法删除分配的对象。如果您不希望程序的内存使用量随着时间的推移而增长(并可能在某个时候耗尽其主机的 RAM),那么至少保留一个指向您动态分配的每个对象的指针很重要,这样您就可以保留使用完对象后能够delete 对象。

[在另一种情况下]我们删除了一个对象,但指针仍然指向地址 前一个对象的位置。在哪些情况下可能有用?

拥有一个指向已删除对象的指针几乎从来没有用过——试图取消引用指针会导致undefined behavior,从技术上讲,甚至读取悬空指针的值(不取消引用)也是一个禁忌( !)。不将指针设置为 NULL 的唯一原因是因为将指针设置为 NULL 将需要额外的 CPU 指令(以及额外的内存写入)来执行,并且 C++ 试图不强迫您支付您不支付的间接费用需要付费。因此,如果您可以保证在删除对象后永远不会尝试使用“悬空指针”,那么不将其设置为 NULL 比无缘无故地将其设置为 NULL 稍微高效一些。

如果以上所有内容对您来说有点程序员不友好,那么您是完全正确的——确实如此。原始指针要求程序员做出非常严格的行为,如果程序员没有 100% 正确处理它们,结果就是程序有问题(内存泄漏、运行时崩溃或其他未定义但不受欢迎的行为)。如果您希望避免这种风险(并且您应该!),那么您可以使用像@987654325 这样的“智能指针”来保存它,而不是使用原始指针(例如Class *)来保存动态分配的对象@ 或 std::shared_ptr。智能指针类将为您提供原始指针所没有的“它会自动执行正确的操作”行为,但代价是为您的程序增加了微不足道的开销。

【讨论】:

    猜你喜欢
    • 2023-03-29
    • 1970-01-01
    • 2016-05-24
    • 2014-09-22
    • 2017-11-09
    • 2012-05-21
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    相关资源
    最近更新 更多