如图here所示,可以使用dynamic_cast
来检测已删除的指针:
#include <iostream>
using namespace std;
class A
{
public:
A() {}
virtual ~A() {}
};
class B : public A
{
public:
B() {}
};
int main()
{
B* pB = new B;
cout << "dynamic_cast<B*>( pB) ";
cout << ( dynamic_cast<B*>(pB) ? "worked" : "failed") << endl;
cout << "dynamic_cast<B*>( (A*)pB) ";
cout << ( dynamic_cast<B*>( (A*)pB) ? "worked" : "failed") << endl;
delete pB;
cout << "dynamic_cast<B*>( pB) ";
cout << ( dynamic_cast<B*>(pB) ? "worked" : "failed") << endl;
cout << "dynamic_cast<B*>( (A*)pB) ";
cout << ( dynamic_cast<B*>( (A*)pB) ? "worked" : "failed") << endl;
}
输出:
dynamic_cast<B*>( pB) worked
dynamic_cast<B*>( (A*)pB) worked
dynamic_cast<B*>( pB) worked
dynamic_cast<B*>( (A*)pB) failed
它解释了检测到vtable的删除。
但我想知道这是怎么可能的,因为我们不会覆盖释放的内存?
该解决方案是否完全可移植?
由于
答案 0 :(得分:6)
首先,尝试以任何形式使用已删除的对象会导致未定义的行为:无论您看到什么结果都可能发生!
观察到的行为的原因很简单,一个对象在销毁期间改变了类型:从作为具体类型的对象,它改变了层次结构中的所有类型。在每个点,虚函数都会改变,vtable(或类似的)会被替换。 dynamic_cast<...>()
只是检测到对象位置处的字节变化。
如果您想要证明此技术无法可靠地工作,您可以将已删除内存的内容设置为随机位模式或最派生类型的对象的位模式:随机位模式可能会导致崩溃,memcpy()
可能声称对象仍然存在。当然,由于它是未定义的行为,任何事情都可能发生。
3.8 [basic.life]第5段的一个相关部分:
在对象的生命周期开始之前但在对象占用的存储空间已经被分配之后,或者在对象的生命周期结束之后以及在重用或释放对象占用的存储之前,任何引用的指针可以使用对象将位于或位于的存储位置,但仅限于有限的方式。对于正在建造或销毁的物体,见12.7。否则,这样的指针指的是已分配的存储(3.7.4.2),并且使用指针就像指针的类型为
void*
一样, 定义明确。允许通过这样的指针的间接,但是得到的左值可以仅以有限的方式使用,如下所述。如果出现以下情况,该程序具有未定义的行为:
- ...
- 指针用作dynamic_cast(5.2.7)的操作数。 ...
奇怪的是,dynamic_cast
上最后一颗子弹上的例子没有使用dynamic_cast
。
当然,该对象也可能被释放,在这种情况下,上述保证甚至不适用。