C++ 是如何做到的?运行时系统知道对象何时超出范围
我想知道 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
运行时不会 - 编译器密切关注范围并生成调用析构函数的代码。如果您创建一个简单的测试应用程序并查看生成的反汇编,您将看到显式的析构函数调用。
来自 MSVC 的反汇编片段:
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:
这是在编译时静态知道的
有时,这更困难
This is known statically at compile time
Sometimes, it's more difficult
它与运行时间无关。编译器跟踪每个词法变量的范围,并添加析构函数调用。
It has nothing to do with runtime. The compiler keeps track of scope of every lexical variable, and adds destructor calls.
“调用析构函数”和“释放与变量关联的内存”是完全不同的两件事。
析构函数只是一个函数,当您的对象超出范围或被显式删除时,C++ 可以很好地为您调用该函数。正如其他人所说,编译器会为您生成此内容。它是您清理班级中需要清理的任何内容的便捷方法。
释放与堆栈上的某些内容相关的内存需要了解堆栈的运行方式。调用函数时,只需将这些变量所需的数据量推入堆栈即可为堆栈上的所有内容分配内存。虽然 C++ 规范中没有明确说明,但“推送”实际上只是让指向堆栈顶部的指针指向更高(或更低),以便为额外的变量腾出空间。这是简单的指针加法。当函数返回时,会发生指针减法。
一切都从堆栈中弹出。您可以通过阅读调用约定了解更多信息。
堆内存由程序显式手动控制。除非代码为您执行显式堆管理,否则您需要确保删除所有新内容。
"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.
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.
范围以范围结束。它不会“检测”它,编译器以及时调用析构函数的方式编写代码。
例如,以下代码
将导致机器代码执行类似以下操作:
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
Would result in machine code that does something like this:
当应用程序进入作用域环境(块、函数调用等)时,运行时将该块的上下文(包括局部变量)加载到堆栈上。这是一个实际的堆栈数据结构。随着执行越来越深入嵌套上下文,堆栈变得越来越高。如果
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()
callsfoo()
which callsbar()
, the stack will havemain()
s context on the bottom of the stack, thenfoo
's context, thenbar
'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.您可能误解了一些编程语言的特征。 C++ 没有垃圾收集器,因此它不会决定对象何时超出范围:由用户决定。
在上面的函数中,您在堆中创建一个对象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.
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.