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

C++;运行时系统知道对象何时超出范围 我想知道C++运行时系统是如何检测对象超出范围的,以便 它相应地调用析构函数以释放占用的内存,c++,runtime,destructor,C++,Runtime,Destructor,谢谢。运行时没有-编译器会跟踪作用域并生成调用析构函数的代码。如果您制作一个简单的测试应用程序并查看生成的反汇编,您将看到显式析构函数调用 MSVC中的反汇编代码段: int main() { std::string s1; ... 00971416 call dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::ba

谢谢。

运行时没有-编译器会跟踪作用域并生成调用析构函数的代码。如果您制作一个简单的测试应用程序并查看生成的反汇编,您将看到显式析构函数调用

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)] 
intmain(){
std::字符串s1;
...
00971416调用dword ptr[uuuu imp_std::basic_string::basic_string(979290h)]
...
{
std::字符串s2;
00971440调用dword ptr[uuuu imp_std::basic_string::basic_string(979290h)]
...    
}
00971452调用dword ptr[uuu imp_std::basic_string::~basic_string(979294h)]
...
}
0097146B调用dword ptr[\uuuuuu imp\u std::basic\u string::~basic\u string(979294h)]

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

这在编译时是静态知道的

{
  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 */
}

您可能误解了一些编程语言的特性。C++没有垃圾回收器,所以它不能决定对象何时超出了范围:用户是否。< /P>
int main()
{
    Obj * obj = new Obj;
    Obj obj2;

    delete obj;
}

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

作用域以作用域结尾。它不会“检测”它,编译器以一种会及时调用析构函数的方式编写代码

例如,以下代码

if(something)
{
    MyClass test;
    test.doSomething();
}
将导致执行以下操作的机器代码:

  • 评估跳跃
  • 必要时跳下
  • 为测试分配内存
  • 在测试时调用MyClass的构造函数
  • 测试时调用doSomething
  • 在测试时调用MyClass的析构函数
  • 释放内存
    • “调用析构函数”和“释放与变量相关的内存”是两件完全不同的事情

      析构函数只是一个C++足够好的函数,当对象超出范围或被显式删除时调用它。正如其他人所说,编译器会为您生成这个。这是一种方便的方式,你可以清理你班上任何你需要清理的东西

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

      void foo()
      {
          HerClass y;
          YourClass x; // stack incremented sizeof(YourClass) + sizeof(HerClass)
      
          return; // destructor called, 
                  // then stack decremented sizeof(YourClass) + sizeof(HerClass)
      }
      
      所有东西都从堆栈中弹出。您可以通过阅读了解更多信息


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

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


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

      这个主题在C#上非常有趣:)对象甚至可能在对象终止执行之前就被销毁。@cprogrammer:首先,这个问题与C无关。其次,不,这在C#中也不太可能。我不确定你指的是什么情况。科迪:事实上这是可能的。如果实例方法中的代码不与其他实例成员通信,则不再引用该实例。GC可以自由地运行终结器(如果有的话),并在需要时清理对象,即使该对象仍有代码在执行。“在函数末尾”应该是“在控制结构末尾”。在C/C++中,作用域不一定指函数,而是指控制结构(函数、
      if
      for
      ,等等)。例如,在
      for
      循环的堆栈上分配的对象将在循环的每次迭代结束时销毁。范围是词汇变量的属性,而不是对象。动态分配的对象根本没有作用域。是的,@netcoder和@Voigt,我只是试图用他自己的术语回答这个问题,并遵循我编写的示例@马克,不客气。@johnmcg,谢谢你的回答。这真的很有帮助。你的第二个例子会导致内存泄漏吗?@Mark no。它会在哪里泄漏内存?更不用说我错过了在goto语句中调用s的dtor。