C++;运行时系统知道对象何时超出范围 我想知道C++运行时系统是如何检测对象超出范围的,以便 它相应地调用析构函数以释放占用的内存
谢谢。运行时没有-编译器会跟踪作用域并生成调用析构函数的代码。如果您制作一个简单的测试应用程序并查看生成的反汇编,您将看到显式析构函数调用 MSVC中的反汇编代码段: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
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。