判断对象是否在堆中分配

看下面的一个基类:


class HeapTracked {                  // 混合类; 跟踪 

public:                                       // 从operator new返回的ptr 
  class MissingAddress{};            // 异常类,见下面代码 
  virtual ~HeapTracked() = 0; 
  static void *operator new(size_t size); 
  static void operator delete(void *ptr); 

  bool isOnHeap() const; 


private: 
  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 member 
list<RawAddress> HeapTracked::addresses; 

HeapTracked::~HeapTracked() {} 


void * HeapTracked::operator new(size_t size) 

  void *memPtr = ::operator new(size);  // 获得内存 
  addresses.push_front(memPtr);         // 把地址放到list的前端 
  return memPtr; 


void HeapTracked::operator delete(void *ptr) 


  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 
  }                                          // 分配的,所以抛出一个异常 

}  


bool HeapTracked::isOnHeap() const 

  // 得到一个指针,指向*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指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值