为什么我可以在第二次调用“删除此”指针后运行对象的方法

时间:2013-09-13 10:51:22

标签: c++ pointers delete-operator

#include <iostream>
#include<stdio.h>

using namespace std;

class aaa{

public:
    void methodTest(){

        cout << "line1\n";
        cout << "line2\n";
        cout << "line3\n";

        delete this;

        cout << "line4\n";
        cout << "line5\n";
        cout << "line6\n";
        cout << "line7\n";
        cout << "line8\n";

    }
    virtual ~aaa(){
        cout <<"destrcutor aaa\n";
    }
};


int main(int argc, char**argv) {


    aaa* ptr = new aaa();
    cout <<"============first  time==============\n";
    ptr->methodTest();
    cout <<"============second time==============\n";
    ptr->methodTest();

    return 0;
}

输出

============first  time==============
line1
line2
line3
destrcutor aaa
line4
line5
line6
line7
line8
============second time==============
line1
line2
line3

RUN FINISHED; Segmentation fault; core dumped; real time: 70ms; user: 0ms; system: 0ms

我的问题是为什么我们可以第二次运行ptr->methodTest()? 当第二次立即呼叫ptr->methodTest()时,它应该是coredump吗? 为什么它仍然可以运行直到达到“删除此”

6 个答案:

答案 0 :(得分:3)

  

我的问题是为什么我们可以第二次运行ptr->methodTest()

这是未定义的行为。编译时检查无法阻止错误,因为编译器通常无法跟踪对象的运行时生命周期,并且C ++通常不会强加运行时检查的开销。

  

在第二次立即呼叫ptr->methodTest()时,它应该是coredump吗?

当指针访问时,您可能会遇到保护错误。简单地调用非虚拟成员函数通常不会访问该对象,因此该错误很可能在此时未被检测到。 (并且假设内存不再可访问;实际上,指针通常仍指向可访问的内存,因此即使尝试访问已删除的对象也可能无法检测到。)

答案 1 :(得分:2)

所有非虚拟成员函数都可以这样考虑:

object->mfun(args...)

相当于:

mfun(object, args...)

到目前为止我?好。现在,在调用ptr-&gt; method_test()时,您可以认为自己要调用method_test(ptr)。如您所见,在该调用中实际上没有指针被取消引用。这意味着程序继续执行它的操作,直到删除已删除的指针之类的操作发生。调用虚函数也会引发崩溃。

但请记住,这只是“典型”实施的运作方式。这是所有未定义的行为,可能会启动核武器,而不是看起来工作一段时间。

答案 2 :(得分:1)

删除元素后访问元素是未定义的行为。

你很幸运。

答案 3 :(得分:1)

因为未定义的行为是 - 好的,未定义的。任何事情都可能发生。编译器可以自由地优化包含UB的代码来完成他们想要的任何事情,包括根本不做任何事情,完全按照你的预期做99,9%的时间,并在你完全忘记了几年后抓住你它

它还可以决定格式化您的硬盘或让您的PC加入僵尸网络; - )

参见例如What Every C Programmer Should Know About Undefined Behavior以及this question

中的奇闻轶事

答案 4 :(得分:0)

在发布中删除指针不会清除它指向的内存区域,只是将该区域标记为空闲。从技术上讲,该区域仍然包含您班级的所有数据。 您仍然可以在此指针上调用方法,但最终会得到dangling pointer。通常,您可以调用不涉及可能会或可能不会运行的成员的方法,因为它是未定义的行为。 在调试中,许多编译器使用魔术值(如0xFEEEFEEE)标记释放区域,以便在调试时轻松区分。

通常在使用指针时,建议在删除指针后将指针设置为nullptr以避免悬空指针

答案 5 :(得分:0)

好吧,因为方法本身只是一个获取类实例指针的汇编代码,它可以被执行,实际上,如果它不会做任何事情(例如只是一个空体),它甚至不会崩溃,由于您没有引用任何无效的内存地址,因此当您开始引用已删除实例的某些成员变量时会发生崩溃,因为该内存已被释放。