0
#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,对吗?为什么它仍然能够运行直到达到“删除这个”

4

6 回答 6

3

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

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

ptr->methodTest()第二次调用时应该立即进行coredump,对吗?

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

于 2013-09-13T11:02:09.983 回答
2

所有非虚成员函数都可以这样想:

object->mfun(args...)

相当于:

mfun(object, args...)

跟我到现在?行。现在,当调用 ptr->method_test() 时,您可以认为自己正在调用 method_test(ptr)。如您所见,在该调用中实际上没有取消引用指针。这意味着程序会继续做它所做的事情,直到发生诸如删除已删除的指针之类的事情。调用虚函数也会触发崩溃。

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

于 2013-09-13T10:59:37.723 回答
1

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

你刚刚走运了。

于 2013-09-13T10:53:06.097 回答
1

因为未定义的行为是-嗯,未定义的。任何事情都可能发生。编译器可以自由地优化包含 UB 的代码来做他们想做的任何事情,包括根本不做任何事情,在 99.9% 的时间里做你期望它做的事情,并在几年后当你完全忘记时赶上你它。

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

参见例如What Every C Programmer Should Know About Undefined Behavior和这个问题中的轶事

于 2013-09-13T11:05:18.217 回答
0

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

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

于 2013-09-13T10:55:29.497 回答
0

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

于 2013-09-13T10:57:29.897 回答