C++:为什么不调用析构函数?

5

我在代码中使用了额外的括号。我认为当局部变量的作用域结束后,析构函数应该被调用,但实际上并非如此:

class TestClass {
public:
    TestClass() {
        printf( "TestClass()\n" );
    }
    ~TestClass() {
        printf( "~TestClass()\n" );
    }
};

int main() {
    int a, b, c;
    {
         TestClass *test = new TestClass();
    }
}

它输出:

TestClass()

所以它没有调用TestClass的析构函数,但是为什么呢?如果我手动调用它(delete test),那么它会调用析构函数,对吧。但是为什么在第一个情况下它不调用析构函数呢?


1
因为如果你使用 new 创建一个对象,它只能在调用 delete 时被销毁 - 它不会在超出作用域时被销毁。 - Nbr44
2
@Nbr44:不完全正确。你可以直接调用析构函数。当然,这样做不会释放内存,但它会销毁对象。然后唯一一个正确释放内存并避免未定义行为的方法是,通过定位 new 在原位置上构造一个同类型的新对象,并对其调用 delete。 - Benjamin Lindley
3个回答

15
TestClass *test = new TestClass();

您使用了 new,它创建了一个动态分配的对象(很可能位于堆上)。这种类型的资源需要由您手动管理。管理时,在您使用完后应该使用delete

{
     TestClass *test = new TestClass();
     // do something
     delete test;
}

但对于您大多数的目的和意图,您只需要使用自动存储对象,这将使您免除手动管理对象的烦恼。它还可能会在短暂存在的对象中拥有更好的性能。除非您有真正充分的理由不这样做,否则应始终优先使用它们。

{
     TestClass test;
     // do something
}

然而,如果你需要动态分配对象或指针的语义,最好使用某种机制来封装对象/资源的删除/释放,这样可以为你提供额外的安全性,特别是在使用异常和条件语句时。在你的情况下,最好使用std::unique_ptr

{
     std::unique_ptr<TestClass> test(new TestClass());
     // auto test = std::make_unique<TestClass>();  in C++14

     // do something (maybe you want to pass ownership of the pointer)
}


以下是一个相关链接,可以帮助你决定使用自动存储对象还是动态分配的对象:为什么C++程序员应该尽可能地减少使用“new”?


1
除非你有非常充分的理由不这样做,否则你应该始终优先使用它。但为什么在这里不使用指针如此重要呢?因为你可能会忘记删除它或出现其他重要问题。 - JavaRunner
1
@JavaRunner 是的。而且它们最容易出错,给你不必要的间接性,可能会影响性能,并且代码中有很多 *delete 看起来很丑陋。 - Mark Garcia
2
@JavaRunner 另一个问题,除了云化的生命周期之外,还有云化的所有权:跟踪动态分配对象的指针归谁所有(即谁负责删除它)可能变得困难。值语义比引用语义更容易理解。 - juanchopanza
@JavaRunner,我建议你学习RAII,这是C++编程中非常好的工具。并且阅读这个SO问题:https://dev59.com/-HRC5IYBdhLWcg3wJNmf。 - Mark Garcia
通常情况下(当然也有例外),只有在对象具有身份和特定生命周期由程序逻辑确定时,才使用动态分配。如果没有身份,就像你所说的那样,复制而不是动态分配。@juanchopanza - James Kanze
显示剩余7条评论

8
因为您拥有指向动态分配对象的指针。只有指针超出范围,而不是它指向的对象。您必须调用delete来调用指针所指的对象的析构函数。
尝试使用自动存储对象代替:
{
  TestClass test;
}

在这里,析构函数将在退出作用域时被调用。
在C++中,不建议使用原始指针来动态分配对象,因为它很容易导致资源泄漏,就像您代码示例中展示的那样。如果确实需要指向动态分配对象的指针,则明智的做法是使用智能指针来处理它们,而不是尝试手动处理它们的销毁。

3
这个答案已经足够好了,但是我可以再补充一些内容。
我看到你的代码中使用了Java。在C++中,创建变量和对象时不需要使用关键字new。实际上,当你使用new关键字时,你的对象会在堆上创建,并且离开作用域后并不会被销毁。要销毁它,你需要调用delete,在你的情况下是delete test;
在像你的结构这样的结构中,在离开作用域后,你只是失去了指向对象的指针,因此在离开作用域后,你无法释放内存并调用析构函数,但最终操作系统会在执行exit()指令后调用析构函数。
总之,C++!= Java

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接