#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吗?
为什么它仍然可以运行直到达到“删除此”
答案 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)
好吧,因为方法本身只是一个获取类实例指针的汇编代码,它可以被执行,实际上,如果它不会做任何事情(例如只是一个空体),它甚至不会崩溃,由于您没有引用任何无效的内存地址,因此当您开始引用已删除实例的某些成员变量时会发生崩溃,因为该内存已被释放。