例如:
int main() {
Foo *leedle = new Foo();
return 0;
}
class Foo {
private:
somePointer* bar;
public:
Foo();
~Foo();
};
Foo::~Foo() {
delete bar;
}
编译器会隐式调用析构函数还是会发生内存泄漏?
我是动态内存的新手,所以如果这不是一个可用的测试用例,我很抱歉。
例如:
int main() {
Foo *leedle = new Foo();
return 0;
}
class Foo {
private:
somePointer* bar;
public:
Foo();
~Foo();
};
Foo::~Foo() {
delete bar;
}
编译器会隐式调用析构函数还是会发生内存泄漏?
我是动态内存的新手,所以如果这不是一个可用的测试用例,我很抱歉。
是的,自动变量将在封闭代码块的末尾被销毁。但请继续阅读。
您的问题标题询问当变量超出范围时是否会调用析构函数。想必你想问的是:
Foo 的析构函数会在 main() 结束时被调用吗?
鉴于您提供的代码,该问题的答案是否定的,因为 Foo 对象具有动态存储持续时间,我们很快就会看到。
请注意这里的自动变量是什么:
Foo* leedle = new Foo();
这里,leedle
是将被销毁的自动变量。 leedle
只是一个指针。leedle
指向的东西没有自动存储时长,不会被销毁。所以,如果你这样做:
void DoIt()
{
Foo* leedle = new leedle;
}
您泄漏了分配的内存new leedle
。
您必须 delete
使用已分配的任何内容new
:
void DoIt()
{
Foo* leedle = new leedle;
delete leedle;
}
通过使用智能指针,这变得更加简单和健壮。在 C++03 中:
void DoIt()
{
std::auto_ptr <Foo> leedle (new Foo);
}
或者在 C++11 中:
void DoIt()
{
std::unique_ptr <Foo> leedle = std::make_unique <Foo> ();
}
如上所述,智能指针用作自动变量,当它们超出范围并被销毁时,它们会自动(在析构函数中)delete
被指向的对象。因此,在上述两种情况下,都没有内存泄漏。
让我们在这里尝试澄清一些语言。在 C++ 中,变量具有存储期限。在 C++03 中,有 3 个存储持续时间:
1:自动:具有自动存储持续时间的变量将在封闭代码块的末尾被销毁。
考虑:
void Foo()
{
bool b = true;
{
int n = 42;
} // LINE 1
double d = 3.14;
} // LINE 2
在此示例中,所有变量都具有自动存储期限。两者b
和d
都将在 LINE 2 n
销毁。将在 LINE 1 销毁。
2: static : 一个具有静态存储期的变量会在程序开始前被分配,在程序结束时被销毁。
3:动态:具有动态存储持续时间的变量将在您使用动态内存分配函数(例如,)分配时分配,在您使用动态内存分配函数(例如,new
)销毁时将被销毁delete
。
在我上面的原始示例中:
void DoIt()
{
Foo* leedle = new leedle;
}
leedle
是一个具有自动存储持续时间的变量,将在结束大括号处销毁。leedle
指向的东西具有动态存储持续时间,并且在上面的代码中没有被破坏。您必须调用delete
以释放它。
C++11 还增加了第四个存储持续时间:
4:线程:具有线程存储持续时间的变量在线程开始时分配,在线程结束时释放。
是的,如果一个对象超出范围,析构函数就会被调用。但是
不,在这种情况下不会调用析构函数,因为您只有一个范围内的指针,该指针没有特定的析构函数,因此不会间接调用Foo
's 析构函数。
这个例子是智能指针的应用领域,比如std::unique_ptr
和std::shared_ptr
。这些是实际的类,与原始指针不同,它们具有析构函数,(有条件地)调用delete
指向的对象。
顺便说一句,Foo
的析构函数 deletes bar
, burbar
从未被初始化或分配给指向实际对象的地址,因此 delete 调用将给出未定义的行为,可能会崩溃。
确实会有内存泄漏。超出范围的对象(Foo *)的析构函数被调用,但指向对象的析构函数(您分配的 Foo)没有。
从技术上讲,由于您在主程序中,因此它不是内存泄漏,因为您可以在应用程序未终止时访问每个分配的变量。在这方面,我引用了 Alexandrescu(来自 Modern C++,关于单例的章节)
当您分配累积数据并丢失对它的所有引用时,就会出现内存泄漏。这不是这里的情况:没有任何东西在积累,我们掌握有关分配内存的知识,直到应用程序结束。此外,所有现代
当然,这并不意味着您不应该调用delete
,因为这将是一种非常糟糕(且危险)的做法。
首先注意代码不会编译;new
返回指向在堆上分配的对象的指针。你需要:
int main() {
Foo *leedle = new Foo();
return 0;
}
现在,由于new
使用动态存储而不是自动分配对象,因此它不会在函数末尾超出范围。因此它也不会被删除,并且您已经泄漏了内存。
在这种情况下,当 main 返回它是程序结束时,操作系统将处理释放所有资源。例如,如果这是任何其他功能,则必须使用删除。