Effective_C++:10、如果你写了一个operator new,请对应写一个operator delete

10、如果你写了一个operator new,请对应写一个operator delete

1、何时需要写一个operator new

        缺省版的operator new和delete功能已经足够了,但不可避免的是他可能需要花费更多的内存,来提高效率。对于一些需要动态分配大量小额空间的程序而言,更需要提高效率。

class AirplaneRep {...};//表示一个Airplane对象

class Airplane {
public:
    ...
private:
    AirplaneRep *rep;//指针,指向一个Airplane对象
};

        如上,Airplane只内含一个指针,当调用operator new时获得的内存要比存储该指针的所需内存多,因为要让operator new与operator delete沟通。为了让operator delete知晓operator new分配了多少内存,通常要在他所传回的内存前加挂一些数据,来说明配置的区块大小

        对于小型对象如Airplane而言,这个额外的内存可能比真正需要的内存都多。若软件运行在内存十分宝贵的环境下,用缺省的operator new分配内存就比较奢侈了。此时,可以根据每个Airplane对象的大小相同,撰写一个operator new,而不需要加挂额外数据。

        为此,撰写operator new,一种方法是:先用默认的operator new配置一大块内存,可容纳大量Airplane对象。而每一个Airplane对象需要的内存都从这块内存挖取。剩余尚未被利用的内存用链表来管理:利用union,当内存未被配置时,他是链表的next指针;当内存被配置为Airplane对象时,他是*rep。

class Airplane {
public:
    static void * operator new (size_t size);
    ...
private:
    union {
        AirplaneRep *rep;
        Airplane *next;
    };
    static const int BLOCK_SIZE;
    static Airplane *headofFreeList;
};

        这里,添加了operator new声明、union、常数(指定每个被配置区块的大小)、static指针(记录freelist的头部,整个类共享一个)。

void * Airplane::operator new (size_t size)
{
    if(size != sizeof(Airplane))
        return ::operator new(size);
    Airplane *p = headofFreeList;
    if(p)
        headofFreeList = p->next;
    else {
        Airplane *newBlock = static_castz<Airplane *>(::operator new(BLOCK_SIZE * sizeof(Airplane)));
        for(int i = 1; i < BLOCK_SIZE; ++i)
            newBlock[i].next = &newBlock[i + 1];
        newBlock[BLOCK_SIZE - 1].next = 0;
        p = newBlock;
        headofFreeList = &newBlock[1];
    }
    return p;
}
Airplane *Airplane::headofFreeList;
const int Airplane::BLOCK_SIZE = 512;

2、为啥要对应写一个operator delete

        现在我们已经写了一个operator new,没有声明operator delete。当使用delete时,他会调用::operator delete来处理Airplane::operator new。而::operator delete认为传回的这个指针是带有额外数据的,实际上并没有,必然会导致错误。因此,一并写出operator new和operator delete。

3、对应写一个operator delete

class Airplane {
public:
    ...
    static void operator delete (void *deadObject, size_t size);
};
void Airplane::operator delete (void *deadObject, size_t size)
{
    if(deadObject == 0) return;
    if(size != sizeof(Airplane)) {
        ::operator delete(deadObject);
        return;
    }
    Airplane *carcass = static_cast<Airplane *>(deadObject);
    carcass->next = headofFreeList;
    headofFreeList = carcass;
}
        这里,使用delete并没有释放这些内存,而是把它放回内存池,即freelist内,这些内存仍在我们的掌控之内,故没有发生内存泄漏。同时,内存池的策略会使得即便频繁使用new、delete也不会那么慢。
        由此,可以想象,可以将配置固定大小区块的内存配置器概念包装起来,即用来配置管理固定大小的内存块而不依赖于特定的类,而在具体的类中,只需使用给内存配置器配置管理内存即可。
class Pool {
public:
    Pool (size_t n);
    void * alloc (size_t n);
    void free (void *p, size_t n);
    ~Pool ();
};
class Airplane {
public:
    ...
    static void * operator new (size_t size);
    static void operator delete (void *p, size_t size);
private:
    AirplaneRep *rep;
    static Pool memPool;
};

inline void * Airplane::operator new (size_t size)
{ return memPool.alloc(size); }

inline void Airplane::operator delete (void *p, size_t size)
{ memPool.free(p, size); }
Pool Airplane::memPool(sizeof(Airplane));
        如此,Airplane与内存管理的细节没什么关系,都被隐藏在Pool中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值