判断对象是否在堆中分配

    xiaoxiao2021-11-05  89

    看下面的一个基类:

    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指针。

    转载请注明原文地址: https://ju.6miu.com/read-678034.html

    最新回复(0)