2

人们总是谈论没有new关键字创建的对象在超出范围时如何被销毁,但是当我想到这一点时,似乎这是错误的。也许当变量超出范围时会调用析构函数,但是我们怎么知道它不再占用堆栈中的空间呢?例如,考虑以下情况:

void DoSomething()
{
  {
    My_Object obj;
    obj.DoSomethingElse();
  }
  AnotherFuncCall();
}

执行时是否保证obj不会存入栈AnotherFuncCall?因为人们总是在说它,所以他们说的话一定有一些道理,所以我假设在obj超出范围时必须调用析构函数,before AnotherFuncCall。这是一个公平的假设吗?

4

8 回答 8

3

你混淆了两个不同的概念。

是的,当您的对象离开其封闭范围时,将调用它的析构函数。这是由标准保证的。

不,不能保证该语言的实现使用堆栈来实现自动存储(即您所说的“堆栈分配的对象”。)

由于大多数编译器使用固定大小的堆栈,我什至不确定您的问题是什么。它通常被实现为一个固定大小的内存区域,其中只需移动指针即可“清理”堆栈,因为该内存将很快再次使用。

因此,由于用于实现堆栈的内存区域的大小是固定的,因此无需将对象占用的内存设置为 0 或其他值。它可以在那里生活直到再次需要它,不会造成任何伤害。

于 2012-06-22T20:03:25.863 回答
1

在本地范围内“在堆栈上”创建的对象具有所谓的自动存储持续时间。该标准说:

C++03 3.7.2 自动存储时长

1/ 明确声明为 auto 或 register 或未明确声明为 static 或 extern 的本地对象具有自动存储持续时间。这些对象的存储一直持续到创建它们的块退出。

2/ [注意:这些对象按照 6.7 中的描述进行初始化和销毁​​。]

关于销毁这些对象:

6.7 声明声明

2/ 具有自动存储持续时间(3.7.2)的变量在每次执行声明语句时都会被初始化。在块中声明的具有自动存储持续时间的变量在退出块时被销毁(6.6)。

因此,根据标准,当具有本地范围的对象超出范围时,将调用析构函数并释放存储空间。

天气与否,标准没有说存储是否在堆栈上。它只是说存储被释放,无论它在哪里。

某些架构没有 PC 所具有的堆栈。C++ 旨在用于任何类型的可编程设备。这就是为什么它从不提及堆栈、堆等的任何内容。

在运行 Windows 和用户模式代码的典型 PC 类型平台上,这些自动变量存储在堆栈中。这些堆栈是固定大小的,并在线程启动时创建。当它们被实例化时,它们会占用堆栈上的更多空间,并且堆栈指针会移动。如果你分配了足够多的这些变量,你会溢出堆栈,你的程序会死得很惨。

尝试在 Windows PC 上运行它,看看会发生什么,例如:

int main()
{
    int boom[10000000];
    for( int* it = &boom[0]; it != &boom[sizeof(boom)/sizeof(boom[0])]; ++it )
        *it = 42;
}
于 2012-06-22T20:10:28.817 回答
1

因为人们总是在说它,所以他们所说的肯定有一些道理,所以我假设必须在 obj 超出范围时调用析构函数,然后再调用 AnotherFuncCall。这是一个公平的假设吗?

这是对的。请注意,最后一个问题没有说明堆栈”。实现是否使用堆栈或其他东西取决于实现。

于 2012-06-22T20:05:01.853 回答
1

人们所说的确实是真的。该对象仍保留在内存位置。但是,堆栈的工作方式意味着对象不会堆栈中占用任何内存空间。

在堆栈上分配内存时通常发生的情况是堆栈指针递减sizeof(type)当变量超出范围并释放对象时,堆栈指针递增,从而缩小分配在堆栈上的数据的有效大小。事实上,数据仍然存在于它的原始地址中,它根本没有被破坏或删除

澄清一下,C++ 标准对此一无所知!C++ 标准完全不知道在内存分配方面称为堆栈或堆的任何东西,因为它们是特定于平台的 实现细节

于 2012-06-22T20:07:03.857 回答
1

我相信这取决于在堆栈中创建对象的位置。如果它在底部(假设堆栈向下增长),那么我认为第二个函数可能会覆盖被破坏的对象空间。如果对象在堆栈内,则可能浪费了该空间,因为必须移动所有其他对象。

于 2012-06-22T19:59:19.213 回答
1

您的堆栈不是动态分配和释放的,它就在那里。您的对象构造函数和析构函数将被调用,但您不会取回内存。

于 2012-06-22T20:02:29.623 回答
0

堆栈上的局部变量不会占用额外的内存。系统从每个线程的堆栈中提供一些内存,堆栈上的变量只使用其中的一部分。超出范围后,编译器可以将堆栈的同一部分重用于其他变量(稍后在同一函数中使用)。

于 2012-06-22T20:05:06.770 回答
0
how do we know that it is no longer taking up space in the stack?

我们没有。有办法查看他们是否这样做,但这些是架构和 ABI 特定的。通常,当函数将控制权返回给调用者时,它们会弹出它们压入堆栈的任何内容。C/C++ 保证的是,当高级对象离开作用域时,它将调用它们的析构函数(尽管一些像 MSVC 6 这样的旧 C++ 在它们没有的时候有可怕的错误)。

Is it guaranteed that obj will not be saved on the stack when AnotherFuncCall is executed? 

不,只要符合 ABI 要求,由编译器决定何时以及如何推送和弹出堆栈帧。

于 2012-06-22T20:15:05.557 回答