C++ 是如何做到的?运行时系统知道对象何时超出范围

发布于 2024-10-27 02:03:08 字数 68 浏览 6 评论 0原文

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

谢谢。

I was wondering how the C++ runtime system detects when an object goes out of scope so that
it calls the destructor accordingly to free up the occupied memory.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

伤感在游骋 2024-11-03 02:03:08

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

来自 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)] 

The runtime doesn't - the compiler keeps tabs on scope and generates the code to call the destructor. If you make a simple test application and look at the generated disassembly you'll see explicit destructor calls.

Disassembly snippet from 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)] 
狠疯拽 2024-11-03 02:03:08

这是在编译时静态知道的

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

This is known statically at compile time

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

Sometimes, it's more difficult

{
  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 */
}
×眷恋的温暖 2024-11-03 02:03:08

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

It has nothing to do with runtime. The compiler keeps track of scope of every lexical variable, and adds destructor calls.

素年丶 2024-11-03 02:03:08

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

析构函数只是一个函数,当您的对象超出范围或被显式删除时,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 the destructor" and "freeing the memory associated with the variable" are two different things entirely.

A destructor is simply a function that C++ is nice enough to call for you when your object is going out of scope or is being explicitely deleted. The compiler generates this for you as others have said. Its a convenient way for you to clean up anything in your class that you need to clean up.

Freeing up the memory associated with something on the stack involves learning about how the stack operates. When a function is called, memory is allocated for everything on the stack by simply pushing onto the stack the amount of data needed for those variables. Though not explicitly stated in the C++ spec, the "push" really just involves letting the pointer to the top of the stack point higher (or lower) to make room for the extra variables. This is simple pointer addition. When the function returns, pointer subtraction occurs.

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

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

Everything is popped off the stack. You can learn more about this by reading about calling conventions.

Heap memory is explicitly manually controlled by the program. Unless code is doing the explicit heap management for you, you'll need to make sure you delete everything you new.

忆沫 2024-11-03 02:03:08

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

例如,以下代码

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

将导致机器代码执行类似以下操作:

  • 如果需要,则评估跳转
  • 向下跳转
  • 为测试分配内存
  • 调用测试时 MyClass 的构造函数 测试
  • 时调用 doSomething 测试
  • 时调用 MyClass 的析构函数
  • 释放内存

The scope ends with the scope. It doesn't "detect" it, the compiler writes the code in a way the destructor will be called in time.

E.g. the following code

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

Would result in machine code that does something like this:

  • evaluate the jump
  • jump down if required
  • allocate memory for test
  • call the constructor of MyClass on test
  • call doSomething on test
  • call the destructor of MyClass on test
  • deallocate memory
静谧幽蓝 2024-11-03 02:03:08

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

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

When the application enters a scoped environment (block, function call, etc.), the runtime loads the context (including local variables) for that block onto the stack. This is an actual stack data structure. As execution goes deeper and deeper into nested contexts, the stack gets higher and higher. If main() calls foo() which calls bar(), the stack will have main()s context on the bottom of the stack, then foo's context, then bar's. This is why infinite recursion results in "stack overflow" and throwing exceptions triggers "stack unwinding."

When execution exits that scope, that context is popped off the stack. In C++, popping objects of the stack includes invoking the destructor for those objects. So when bar() returns, its local variables will be popped off the stack, and the destructors for those variables will be invoked.

·深蓝 2024-11-03 02:03:08

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

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

    delete obj;
}

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

You're probably misunderstanding the characteristics of a few programming languages. C++ does not have a garbage collector, so it does not decide when an object has gone out of scope: the user does.

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

    delete obj;
}

In the function above, you create an object obj in the heap, and release it when you're no longer going to use it. The object obj2, on the other hand, just terminates its life at the end of main(), as any other variable. When an object terminates its life, the destructor of the object is called automatically; the compiler inserts the calls to these destructors automatically: at the end of the function, or when operator delete is invoked.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文