- 代码初觉
先看一段代码,想象它的结果是怎样的?
delete p;
p = NULL;
p->DoSomething();
这样的代码不会抛出异常或导致崩溃吗?对象都已经被delete掉而且都设为空了,怎么还可以调用其函数?
- 诡异的非虚函数
但对C++而言,这样的代码是有可能调用成功且不会产生任何异常的。但有一个条件,那就是这个函数必须是非虚函数。
示例代码如下:
class TestClass
{
public:
void Go()
{
cout<<"Go OK!"<<endl;
}
};
int main()
{
TestClass* p = new TestClass();
delete p;
p = NULL;
p->Go();
return 0;
}
输出为:
原因:
该函数的调用地址在编译期间就已经确定,而且非虚函数的地址只跟类定义本身有关,与具体的实例对象无关,所以当对象的内存会销毁掉,并不会影响到对其非虚函数的调用。所以此次函数的调用可以成功。当然,如果该函数内部调用了对象的数据成员,还是会发生崩溃的。
- 不好惹的虚函数
那再来看见虚函数,还会不会在delete之后被调用得到呢?
class TestClass
{
public:
virtual void Go()
{
cout<<"Go OK!"<<endl;
}
};
int main()
{
TestClass* p = new TestClass();
delete p;
p = NULL;
p->Go();
return 0;
}
运行结果:崩溃
其原因就在于虚函数是延迟绑定的,不是在编译期间就确定下来地址的,而是跟对象息息相关的。C++的内存模型里就可以看出,虚函数是通过虚表指针来引用的,而虚表指针式在对象的前4个字节里面。那么当对象被销毁时,其虚表指针就被销毁,虚函数自然是无从引用了。
- 引用成员变量,更是别想了
class TestClass
{
public:
int m_data;
};
int main()
{
TestClass* p = new TestClass();
delete p;
p = NULL;
cout<<p->m_data; // crash!!!!
return 0;
}