删除调用析构函数但不删除对象?

Posted

技术标签:

【中文标题】删除调用析构函数但不删除对象?【英文标题】:Delete calling destructor but not deleting object? 【发布时间】:2013-09-30 04:41:20 【问题描述】:

所以我已经使用 c++ 和指针一年半了,我认为我已经成功了。我之前多次调用过对象的删除,对象实际上被删除了,或者至少我认为它们确实被删除了。

下面的代码简直把我搞糊涂了:

#include <iostream>

class MyClass

public:
    int a;

    MyClass() : a(10) 
        std::cout << "constructor ran\n";
    

    void method(std::string input_) 
        std::cout << param_ << "\n";
    

    ~MyClass() 
        std::cout << "destructor ran\n";
    

;

int main()


   MyClass* ptr = new MyClass;

   ptr->method("1");

   delete ptr;

   ptr->method("2.5");


此代码输出:

constructor ran
1
destructor ran
2.5

我很困惑为什么它没有抛出任何类型的错误 - 我期待内存超出范围异常或类似情况,但没有。 for 循环在那里,以防有某种隐藏的垃圾收集,尽管据我所知 C++ 中没有垃圾收集。

谁能解释为什么这段代码可以工作,或者我在哪里出错了,因为它不会给我错误?

【问题讨论】:

它完全是偶然的。在 deleted 之后使用指针是错误的。它可能会做你认为正确的事情;它可能不会。 delete 之后使用指针会进入未定义行为领域。 您可能希望在删除 nullptr 值后将其设置为指针,以查看您的预期行为。 【参考方案1】:

在这段代码中:

MyClass* ptr = new MyClass;
ptr->method("1");
delete ptr;
ptr->method("2.5");

您正在访问已被释放的内存,这会产生未定义的行为,这意味着任何事情都可能发生,包括最坏的情况:它似乎可以正常工作.

这就是为什么最好将这样的指针设置为NULL 而不是允许这种事情发生的原因之一:

delete ptr;
ptr = NULL;

虽然最好的办法是尽可能完全避免使用指针。

【讨论】:

【参考方案2】:

您正在访问内存,它在被另一个新调用使用之前不会重置。但是,每次调用 delete 时,最好将指针设置为NULL

【讨论】:

【参考方案3】:

您误解了 delete 的作用。 delete 所做的只是调用析构函数,并告诉分配器该内存是空闲的。它不会改变实际的指针。除此之外的任何内容都是未定义的。

在这种情况下,它对指向的实际数据没有任何作用。该指针指向它之前指向的相同数据,并且在其上调用方法就可以了。但是,不能保证这种行为;事实上,它是明确未指定的。 delete 可以将数据归零;或者分配器可以为其他东西分配相同的内存,或者编译器可以拒绝编译它。

为了性能,C++ 允许您做许多不安全的事情。这是其中之一。如果你想避免这种错误,最好这样做:

delete ptr;
ptr = NULL;

以确保您不会尝试重用指针,如果这样做会立即崩溃,而不是出现未定义的行为。

【讨论】:

"调用它的方法工作得很好" 不准确,有很多情况下它不能“工作得很好”。正如其他人所提到的,重点是允许 UB 做任何事情,包括看起来工作。 @syam 我的意思是在他演示的情况下它工作正常,并不是说这是一件好事。我已经扩展了我的答案以澄清。我倾向于发现解释实际发生的事情(运行时只是简单地保留数据,并且当您调用它的操作方法就像在删除之前一样)比仅仅说它是未定义的行为并且任何事情都可能发生更有帮助(尽管后者在技术上是正确的)。 @syam 我已经添加了进一步的解释以澄清行为未定义。 嗯,解释发生了什么的问题确实在于它不是普遍正确的:在另一个实现中它可能不会做同样的事情。因此我的吹毛求疵。 ;) 另一个编译器可能会订购比萨饼或格式化您的硬盘驱动器,它仍然符合标准(但您已经知道,我主要是为了 OP 的利益发表评论)。 @Gunshin 是的,有一些错误被捕获,例如尝试访问空指针(或与 NULL 有小偏移的指针)。并且访问尚未映射的内存是错误的。但是不能保证访问无效指针会导致错误;这就是为什么 C 和 C++ 被认为是“不安全”或“非托管”语言,而 Java 或 C# 等语言被认为是“安全”或“托管”语言,因为它们不允许您这样做。【参考方案4】:

delete ptr 之后调用ptr-&gt;method("2.5") 具有undefined behaviour。这意味着任何事情都可能发生,包括您所观察到的。

【讨论】:

以上是关于删除调用析构函数但不删除对象?的主要内容,如果未能解决你的问题,请参考以下文章

delete了,析构函数却没有调用

删除指向子类的指针会调用基类析构函数吗?

cpp中的析构函数会自动调用吗?即使析构函数没有提及非动态变量,它们是不是也会被删除?

未调用 QCoreApplication 析构函数

当类没有析构函数时,智能指针或作用域指针会删除对象吗

为啥在析构函数中抛出异常时不调用重载删除?