【问题标题】:C++: why it doesn't call a destructor?C++:为什么它不调用析构函数?
【发布时间】:2013-08-08 11:32:26
【问题描述】:

我在代码中使用了额外的括号。我认为在局部变量范围结束后应该调用析构函数,但它不是这样工作的:

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

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

它输出:

TestClass()

所以它没有调用 TestClass 的析构函数,但为什么呢?如果我手动调用它(删除测试),它会调用析构函数,对。但是为什么它在第一种情况下不调用析构函数呢?

【问题讨论】:

  • 因为如果你用new创建一个对象,它只能在调用delete时被破坏——它不会因为超出范围而被破坏。
  • @Nbr44:不完全正确。您可以直接调用析构函数。当然,这不会释放内存,但会破坏对象。那么,在没有未定义行为的情况下正确释放内存的唯一方法是在其位置构造另一个相同类型的对象(通过放置 new)并在其上调用 delete。

标签: c++ class destructor


【解决方案1】:

因为你有一个指向动态分配对象的指针。只有指针超出范围,而不是它指向的对象。您必须在指针上调用 delete 才能调用指针对象的析构函数。

尝试使用自动存储对象:

{
  TestClass test;
}

这里,析构函数在退出作用域时被调用。

不鼓励在 C++ 中使用指向动态分配对象的原始指针,因为它很容易导致资源泄漏,就像代码示例中显示的那样。如果确实需要指向动态分配对象的指针,明智的做法是使用smart pointer 处理它们,而不是尝试手动处理它们的销毁。

【讨论】:

    【解决方案2】:
    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)
    }
    


    以下是一个相关链接,可帮助您决定是使用自动存储对象还是动态分配对象:Why should C++ programmers minimize use of 'new'?

    【讨论】:

    • "除非您有充分的理由不这样做,否则您应该始终更喜欢使用它。" -- 但是为什么不在这里使用指针如此重要呢?因为你可能忘记删除它或其他一些重要原因?
    • @JavaRunner 是的。而且它们最容易出错,给您带来可能会影响性能的不必要的间接性,并且代码中包含大量 *s 和 deletes 是很难看的。
    • @JavaRunner 另一个问题,除了云的生命周期之外,是云所有权:很难跟踪谁拥有指向动态分配对象的指针,即谁负责删除它。值语义比引用语义更容易推理。
    • @JavaRunner 我推荐你学习RAII,这是一个非常好的C++编程工具。另请阅读这个 SO 问题:stackoverflow.com/questions/395123/raii-and-smart-pointers-in-c
    • 您使用unique_ptr 选择了一个不好的示例,因为它产生的语义与使用局部变量完全相同。例如,如果您使用多态性(实际类型直到运行时才知道),这可能是合适的,但是动态分配的最常见原因正是因为所需的生命周期不对应于任何给定的范围,并且必须明确地销毁对象,这取决于程序逻辑,而不是像范围这样的词法考虑。
    【解决方案3】:

    This answer 已经够用了,不过还得再补充一点。

    我看到你的编码是Java。在C++ 中,不需要在堆栈关键字new 中创建变量/对象。实际上,当您使用关键字new 时,您的对象是在堆中创建的,并且在离开范围后不会破坏。要销毁它,您需要在您的情况下调用delete delete test;

    在像你这样的结构中,离开作用域后你只是失去指向对象的指针,所以离开作用域后你不能释放内存并调用析构函数,但最终操作系统在exit()指令执行后调用析构函数。

    总结一下C++ != Java

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-21
      • 1970-01-01
      • 2016-10-15
      • 2014-06-13
      • 1970-01-01
      • 2021-06-21
      相关资源
      最近更新 更多