当一个对象超出范围时,是否会调用析构函数?

时间:2013-07-17 14:45:03

标签: c++ destructor dynamic-memory-allocation

例如:

int main() {
    Foo *leedle = new Foo();

    return 0;
}

class Foo {
private:
    somePointer* bar;

public:
    Foo();
    ~Foo();
};

Foo::~Foo() {
    delete bar;
}

编译器是否会隐式调用析构函数,还是会发生内存泄漏?

我是动态内存的新手,所以如果这不是一个可用的测试用例,我很抱歉。

5 个答案:

答案 0 :(得分:88)

是的,自动变量将在封闭代码块的末尾被销毁。但继续阅读。

您的问题标题询问当变量超出范围时是否会调用析构函数。大概你要问的是:

  

将在main()的末尾调用Foo的析构函数吗?

鉴于您提供的代码,该问题的答案是,因为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

在此示例中,所有变量都具有自动存储持续时间。 {2} bd都将在第2行销毁。n将在第1行销毁。

2:静态:具有静态存储持续时间的变量将在程序开始之前分配,并在程序结束时销毁。

3:动态:当您使用动态内存分配函数(例如new)分配动态存储持续时间的变量时,将分配该变量,并在使用动态内存分配函数(例如delete)。

在我上面的原始示例中:

void DoIt()
{
  Foo* leedle = new leedle;
}

leedle是一个具有自动存储持续时间的变量,将在结束时销毁。 leedle指向的内容具有动态存储持续时间,并且未在上面的代码中销毁。您必须致电delete才能解除分配。

C ++ 11还增加了第四个存储持续时间:

4:线程:线程开始时分配具有线程存储持续时间的变量,并在线程结束时分配。

答案 1 :(得分:6)

是的,如果对象超出范围,则会调用析构函数。的 BUT 不,在这种情况下不会调用析构函数,因为在作用域中只有指针,该指针没有特定的析构函数,因此不会间接调用Foo'析构函数。

此示例是智能指针的应用程序域,如std::unique_ptrstd::shared_ptr。那些是实际的类,不像原始指针一个析构函数,(有条件地)在指向对象上调用delete

顺便说一句,Foo的析构函数删除bar,bur bar从未被初始化,也没有分配给指向实际对象的地址,因此删除调用将给出未定义的行为,可能是崩溃。

答案 2 :(得分:2)

确实会发生内存泄漏。超出范围(Foo *)的对象的析构函数被调用,但指向对象(您分配的Foo)的析构函数不会被调用。

从技术上讲,由于您处于主要状态,因此不会发生内存泄漏,因为直到应用程序未终止时,您才能访问每个已分配的变量。在这方面,我引用了Alexandrescu(来自Modern C ++,关于单身人士的章节)

  

分配累积数据并丢失所有数据时,会出现内存泄漏   引用它。这不是这种情况:没有任何积累,   我们掌握有关分配内存的知识,直到结束   应用。此外,所有现代

当然,这并不意味着你不应该致电delete,因为这将是一个非常糟糕(和危险)的做法。

答案 3 :(得分:1)

首先请注意代码不会编译; new返回指向堆上分配的对象的指针。你需要:

int main() {
    Foo *leedle = new Foo();
    return 0;
}

现在,由于new使用动态存储而不是自动分配对象,因此它不会超出函数末尾的范围。因此,它也不会被删除,并且你已经泄露了内存。

答案 4 :(得分:-5)

在这种情况下,当main返回它是程序的结尾时,操作系统将处理释放所有资源。例如,如果这是任何其他功能,您将不得不这样做 使用删除。