C ++运行时系统如何知道对象何时超出范围

时间:2011-03-26 20:26:15

标签: c++ runtime destructor

我想知道C ++运行时系统如何检测对象何时超出范围 它相应地调用析构函数来释放占用的内存。

感谢。

7 个答案:

答案 0 :(得分:25)

运行时没有 - 编译器会对范围进行标记并生成调用析构函数的代码。如果您创建一个简单的测试应用程序并查看生成的反汇编,您将看到显式的析构函数调用。

来自MSVC的反汇编代码段:

int main() {
    std::string s1;
...
00971416  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (979290h)] 
...
    {
        std::string s2;
00971440  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (979290h)] 
...    
    }
00971452  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (979294h)] 
...
}
0097146B  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (979294h)] 

答案 1 :(得分:17)

这在编译时是静态知道的

{
  string s; /* ctor called here */
} /* dtor called here */

有时候,这更难了

{
  again:
  {
    string s; /* ctor called here */
    goto again; /* now dtor of s is called here */
    string q; /* ctor not called. not reached. */
  } /* dtor of s and q would be called here. but not reached */
}

答案 2 :(得分:6)

它与运行时无关。编译器跟踪每个词法变量的范围,并添加析构函数调用。

答案 3 :(得分:5)

范围以范围结束。它不会“检测”它,编译器会以及时调用析构函数的方式编写代码。

E.g。以下代码

if(something)
{
    MyClass test;
    test.doSomething();
}

会导致机器代码执行以下操作:

  • 评估跳跃
  • 如果需要,
  • 跳下来
  • 为测试分配内存
  • 在test
  • 上调用MyClass的构造函数
  • 在测试中调用doSomething
  • 在测试中调用MyClass的析构函数
  • 释放内存

答案 4 :(得分:5)

“调用析构函数”和“释放与变量相关的内存”完全是两件事。

析构函数只是一个函数,当你的对象超出范围或被明确删除时,C ++足以为你调用。正如其他人所说,编译器为您生成了这个。它是一种方便的方法,可以清理班级中需要清理的任何内容。

释放与堆栈上的内容相关联的内存涉及了解堆栈的运行方式。调用函数时,只需将堆栈中所有变量所需的数据量推入堆栈,即可为堆栈中的所有内容分配内存。虽然在C ++规范中没有明确说明,但“推”实际上只是让指向堆栈顶部的指针更高(或更低),以便为额外变量腾出空间。这是简单的指针添加。函数返回时,发生指针减法。

void foo()
{
    HerClass y;
    YourClass x; // stack incremented sizeof(YourClass) + sizeof(HerClass)

    return; // destructor called, 
            // then stack decremented sizeof(YourClass) + sizeof(HerClass)
}

所有东西都从堆栈中弹出。您可以通过阅读calling conventions了解更多相关信息。

堆内存由程序明确手动控制。除非代码为您执行显式堆管理,否则您需要确保删除新的所有内容。

答案 5 :(得分:3)

当应用程序进入作用域环境(块,函数调用等)时,运行时将该块的上下文(包括局部变量)加载到堆栈中。这是一个实际的堆栈数据结构。随着执行越来越深入到嵌套上下文中,堆栈越来越高。如果main()调用调用foo()的{​​{1}},则堆栈底部会有bar()个上下文,然后main()的上下文,然后{ {1}}的。这就是无限递归导致“堆栈溢出”并抛出异常触发“堆​​栈展开”的原因。

当执行退出该范围时,该上下文将从堆栈中弹出。在C ++中,弹出堆栈对象包括为这些对象调用析构函数。因此,当foo bar时,它的局部变量将从堆栈中弹出,并且将调用这些变量的析构函数。

答案 6 :(得分:1)

你可能误解了一些编程语言的特性。 C ++没有垃圾收集器,因此它不会决定对象何时超出范围:用户会这样做。

int main()
{
    Obj * obj = new Obj;
    Obj obj2;

    delete obj;
}

在上面的函数中,您在堆中创建了一个对象 obj ,并在您不再使用它时将其释放。另一方面,对象 obj2 只是在 main()的末尾终止它的生命,就像任何其他变量一样。当一个对象终止它的生命时,会自动调用该对象的析构函数;编译器自动插入对这些析构函数的调用:在函数末尾,或者调用运算符 delete 时。