【问题标题】:When does an object on the heap go out of scope堆上的对象何时超出范围
【发布时间】:2011-12-16 17:55:09
【问题描述】:

考虑以下程序:

int main() {

    while(...) {
        int* foobar = new int;
    }

    return 0;
}

foobar 何时超出范围

我知道在使用new时,属性是在堆上分配的,需要用delete手动删除,在上面的代码中,它导致了内存泄漏。但是,范围呢?

我认为一旦 while 循环终止它就会超出范围,因为您不再可以直接访问它。例如,您不能在循环终止后delete 它。

【问题讨论】:

  • 这很棘手,因为foobar 是一个指针,它正确地超出范围并被销毁(但它指向的对象没有被释放)。 int 是堆上的一个对象,没有作用域,并且由于您没有 delete 调用,所以它被泄露了。

标签: c++ scope


【解决方案1】:

这里要小心,foobar 在 while 循环中是本地的,但是堆上的分配没有作用域,只有在你调用 delete 时才会被破坏。

就编译器而言,变量和分配没有以任何方式链接。实际上,分配发生在运行时,因此编译器甚至看不到它。

【讨论】:

  • @MooingDuck:我推荐int ;-p
【解决方案2】:

foobar 是一个局部变量,在块的末尾超出范围。

*foobar 是一个具有手动生命周期的动态分配对象。由于它没有作用域的生命周期,所以这个问题没有意义——它没有一个可以超出的作用域。它的生命周期是手动管理的,并且对象一直存在,直到你delete它。

您的问题带有危险的偏见和成见。最好以干净的头脑和开放的态度来接触 C++。只有这样,您才能充分领略语言的奇妙之处。


这里是干净和开放的方法:考虑 1)存储类(自动、静态、动态),2)对象生命周期(范围、永久、手动),3)对象语义(值(副本)与引用(别名)),4)RAII 和单一职责类。 清除你的想法 a) stack/heap, b) pointers, c) new/delete, d) 析构函数/复制构造函数/赋值运算符.

【讨论】:

  • 你不能完全清除你对赋值运算符的想法,因为即使你把其他一切都做对了,你也必须明确地 default 移动 ctor 并为你的值语义类移动赋值:- )
  • +1 表示干净和开放的方法,尽管它应该使用更大的字体而不是更小的字体;-P
  • @SteveJessop:常规顺序一如既往:一旦你明白为什么你可以忽略建议,忽略建议是安全的 :-) (尽管如果你不声明 anything ,你应该自动获得最优的移动语义,不是吗?)
  • @Kerrek:你说得对,我记错了隐式声明的移动构造函数的条件。我认为只要你只使用值语义类作为数据成员(加上shared_ptr,如果绝对必要的话),你什么都不做。
  • @SteveJessop:“什么都不做”是我的人生目标。 C++ 只是我到达那里的方式。
【解决方案3】:

这是一个非常棒的内存泄漏。堆栈上有一个变量指向堆上分配的内存。在 while 循环范围结束时,您需要在丢失对它的引用之前删除堆上的内存。或者,如果您不想对内存管理大惊小怪,请始终使用智能指针来拥有堆上的原始内存并让它自行清理。

#include <memory>
int main() {

    while(...) {
        std::unique_ptr<int> foobar = new int;
    } // smart pointer foobar deletes the allocated int each iteration

    return 0;
}

【讨论】:

    【解决方案4】:

    当程序到达 while 循环的右大括号时,指针 (foobar) 将超出范围。因此,如果... 中的表达式仍然为真,那么每次执行循环时都会泄漏内存,因为您在右括号中丢失了分配对象的句柄。

    【讨论】:

    • 您使用itthat variable,也没有澄清。指针超出范围,指针/对象将被泄露,因为它没有范围。
    • 问:“foobar 什么时候超出范围?”,答:“它会超出范围......”我显然是在谈论 foobar。唯一有问题的变量是 foobar,指针/对象不是变量。无论如何更新...
    • 虽然答案是正确的,但它具有误导性,因为 OP 显然没有区分指针和指针。
    【解决方案5】:

    这里foobar 是一个int 指针,占用堆栈中的内存。您使用new 动态创建的int 实例进入堆。当foobar 超出范围时,您将丢失对它的引用,因此您无法删除在堆中分配的内存。

    最好的解决办法是:

    while(--)
    {
        int foobar;
    }//it goes out of scope here. deleted from stack automatically!!
    

    如果您仍想使用动态分配,请执行以下操作:

    while(--)
    {
        int* foobar=new int;
        //do your work here!
        delete foobar; //This deletes the heap memory allocated!
        foobar=NULL;   //avoid dangling pointer! :)
    }
    

    【讨论】:

      【解决方案6】:

      foobar 在每次循环交互后超出范围。

      您分配和分配给foobar 的内存正在泄漏,因为它仍然在堆上分配,但程序中没有对它的引用。

      【讨论】:

        【解决方案7】:

        由于 foobar 是在循环体中声明的,因此在循环的每次迭代结束时它都会超出范围。然后重新声明它,并一次又一次地分配新内存,直到循环结束。 foob​​ar 指向的实际对象永远不会超出范围。范围不适用于动态分配(也称为堆)对象,仅适用于自动(堆栈)对象。

        【讨论】:

          【解决方案8】:

          Foobar 指针是在栈上创建的,而新的 int 是在堆上创建的。在 while 循环的情况下,每次代码循环时, foobar 都会超出范围。新创建的 int 保留在堆上。每次迭代都会创建一个新的 int,并重置指针,这意味着指针不再可以访问堆上的任何以前的 int(s)。

          在之前的每一个答案中,甚至在这个答案中,似乎都缺少的是堆超出范围。也许,我得到的术语不正确,但我知道在某些时候堆也被重置了。一旦程序不再运行,或者计算机关闭时,它可能会发生,但我知道它会发生。

          让我们从不同的角度来看这个问题。我编写了许多程序,它们会泄漏内存。多年来,我拥有我的电脑,我很肯定,我已经泄漏了超过 2 GB 的内存。我的电脑只有 1 gig 的内存。因此,如果堆永远不会超出范围,那么我的计算机就有了一些神奇的内存。你们中有人愿意解释一下堆何时超出范围吗?

          【讨论】:

            猜你喜欢
            • 2011-08-10
            • 2012-04-22
            • 2018-02-23
            • 2011-11-23
            • 2018-05-02
            • 2015-01-26
            • 1970-01-01
            • 2012-04-14
            • 2015-01-14
            相关资源
            最近更新 更多