看下面的一个基类:
class HeapTracked { // 混合类; 跟踪
public: // 从operator new返回的ptrclass MissingAddress{}; // 异常类,见下面代码
virtual ~HeapTracked() = 0;
static void *operator new(size_t size);
static void operator delete(void *ptr);
bool isOnHeap() const;
typedef const void* RawAddress;
static list<RawAddress> addresses;
};
这个类使用了list(链表)数据结构跟踪从operator new返回的所有指针,
list是标准C++库的一部分 。operator new函数分配内存并把地址加入到list中;
operator delete用来释放内存并从list中移去地址元素。isOnHeap判断一个对象的地址是否在list中。
HeapTracked类的实现很简单,调用全局的operator new和operator delete函数来完成内存的分配与释放,list类里的函数进行插入操作和删除操作,并进行单语句的查找操
作。
以下是HeapTracked的全部实现:
// mandatory definition of static class memberlist<RawAddress> HeapTracked::addresses;
HeapTracked::~HeapTracked() {}
{
void *memPtr = ::operator new(size); // 获得内存
addresses.push_front(memPtr); // 把地址放到list的前端
return memPtr;
}
{
list<RawAddress>::iterator it =
find(addresses.begin(), addresses.end(), ptr);
if (it != addresses.end()) { // 如果发现一个元素
addresses.erase(it); //则删除该元素
::operator delete(ptr); // 释放内存
} else { // 否则
throw MissingAddress(); // ptr就不是用operator new
} // 分配的,所以抛出一个异常
}
{
// 得到一个指针,指向*this占据的内存空间的起始处,
const void *rawAddress = dynamic_cast<const void*>(this);
// 在operator new返回的地址list中查到指针
list<RawAddress>::iterator it =
find(addresses.begin(), addresses.end(), rawAddress);
return it != addresses.end(); // 返回it是否被找到
}
尽管你可能对list类和标准C++库的其它部分不很熟悉,代码还是很一目了然。不过代码里的注释已经能够解释这个例子是如何运行的。 只有一个地方可能让你感到困惑,就是这个语句(在isOnHeap函数中)
const void *rawAddress = dynamic_cast<const void*>(this);
我前面说过带有多继承或虚基类的对象会有几个地址,这导致编写全局函数
isSafeToDelete会很复杂。这个问题在isOnHeap中仍然会遇到,但是因为isOnHeap仅仅用于HeapTracked对象中,我们能使用dynamic_cast操作符的一种特殊的特性来消除这个问题。只需简单地放入dynamic_cast,把一个指针dynamic_cast成void*类型(或const void*或volatile void* 。 。 。 。 。 ) ,生成的指针将指向“原指针指向对象内
存”的开始处。但是dynamic_cast只能用于“指向至少具有一个虚拟函数的对象”的指针
上。我们该死的isSafeToDelete函数可以用于指向任何类型的指针,所以dynamic_cast
也不能帮助它。isOnHeap更具有选择性(它只能测试指向HeapTracked对象的指针) ,所以能把this指针dynamic_cast成const void*,变成一个指向当前对象起始地址的指针。如果HeapTracked::operator new为当前对 象分配内存,这个指针就是
HeapTracked::operator new返回的指针。如果你的编译器支持dynamic_cast 操作符,这个技巧是完全可移植的。
使用这个类,即使是最初级的程序员也可以在类中加入跟踪堆中指针的功能。他们所需
要做的就是让他们的类从HeapTracked继承下来。例如我们想判断Assert对象指针指向的是否是堆对象:
class Asset: public HeapTracked {
private:
UPNumber value;
...
};
我们能够这样查询Assert*指针,如下所示:
void inventoryAsset(const Asset *ap)
{
if (ap->isOnHeap()) {
ap is a heap-based asset — inventory it as such;
}
else {
ap is a non-heap-based asset — record it that way;
}
}
像HeapTracked这样的混合类有一个缺点,它不能用于内建类型,因为象int和char
这样的类型不能继承自其它类型。 不过使用象HeapTracked的原因一般都是要判断是否可以调用“delete this”,你不可能在内建类型上调用它,因为内建类型没有this指针。