分类: C/C++
2013-01-21 22:21:45
一般来说,我们不会对全局的new/delete操作符进行重载,而是对某个特定的class。这样我们不会改变软件域中,开发人员和维护人员对new和delete的理解。
在这篇文章中,我将会用演绎的方式来阐述一种设计模式,用于提供功能性的复用,我们叫它“功能模式”。
基本的情境是这样的:在一个软件系统中,我们可以构建一个叫memory
pool的container,每个这样的container可以为我们分配一个固定大小的内存块,以此来加快内存的分配。因为每个memory
pool是为某个特定的一个类或者一组类(具有继承关系)服务的,这样也会减少分配这些对象是的竞争情况(现在没有那个软件还是单线程的吧??)。
我们要做到概述中描述的情境,必须做以下几件事情:
1. 声明一个memory pool 指针
2. 寻找某个初始化点构建一个memory pool, 并把声明的指针指向它
3. 重载某个类的new和delete,或者在基类中重载new和delete
假设memory pool 的构建函数需要以下信息:
MemPool *buildMemPool(char const *name, const size_t size);
Memory pool的导出接口有:
MemPool::allocate(); //由于每个Pool只能分配一种size的对象,所以这里不需要size参数
MemPool::release(void *p);
我想做到以上三点,是件很容易的事情,比如:
xxx.hxx Class A{ Private: Static MemPool *mMemPool; Public: Static operator new (size_t s) { mMemPool->allocate();} Static operator delete(void *p) { mMemPool->release(p); } }; Xxx.cxx MemPool *A::mMemPool = NULL; Void init(){ A::mMemPool = buildMemPool(“AMemPool”, sizeof(A)); }
现在问题来了,如果你有三四十个class需要这么做,你不是产生了一大堆的重复代码?如果你回答我说,我不在乎,只要能工作就行,管它呢。那么我劝你早点转行,或者至少不需要再浪费时间看这篇文章了。
为了减少这样的重复代码,我们可能会想到用MACRO来声明这些重复代码,这样带来的一个好处就是,如果我想为所有的用这个memory
pool的class加点什么,只需要修改这个宏。
所有下面的东西出来了:
#define POOL_DECLARE_OPERATOR(typ) \
static MemPool *privatePoolPtr; \
void *operator new(size_t s) { return thePoolPtr->allocate(); } \
void operator delete(void * p) { thePoolPtr->release(p); }
好, 我想你们应该知道,如何使用这个宏。这里不在赘述。
这个宏其实并没有解决什么实际问题,其实。而且还会让读代码的人感到疑惑。因为客户还需要在他们的代码中使用privatePoolPtr去初始化它。
嗯,也许有人想到了下面一种更好的方法:
#define POOL_DECLARE_OPERATOR(typ) \
static MemPool privatePoolPtr; \ <===================这里直接使用对象,而不是指针
void *operator new(size_t s) { return thePoolPtr.allocate(); } \
void operator delete(void * p) { thePoolPtr.release(p); }
似乎所有的问题都解决了,但是你带来了更严重的问题,初始化依赖。比如你所创建的memory pool需要加入到某个global
list,以方便管理,如果你这个pool在被初始化时,那个global
list的header还没被初始化,将会发生什么事情?答案是:什么事情都有可能,最不好的就是似乎什么都没发生。
那么我们有没有更好的办法来处理这个事情呢?
先来看看代码:
templateclass MemPoolAllocator { public: virtual ~MemPoolAllocator() { } static void* operator new(size_t s) { static _assister MemPoolAllocatorPrivatePool; Pool_assert(mPrivatePool->verifyRequestedSize(s)); return mPrivatePool->allocate(); } static void operator delete(void *p) { if(p == NULL) return; Pool_assert(mPrivatePool!= NULL); mPrivatePool->release(p); } protected: Pool* const getPrivatePool() { return mPrivatePool; } private: class _assister{ public: _assister() { static int gardFlag = 1; if(mp_atomic_dec_and_tst((volatile atomic_t *)&gardFlag)) { mPrivatePool = BuildMemPool(poolName, objSize==0?sizeof(clientType):objSize); } } }; static MemPool* mPrivatePool; }; template MemPool* MemPoolAllocator ::mPrivatePool = NULL;
如何使用这个模板类呢?比如
xxx.cxx
extern char const poolName[] = “AMemPool”; < =======定义一个外部连接的字符串,以便被模板用
xxx.hxx
extern char const poolName[]; < ==== 声明
Class A: public template {
};
就这么简单,A的对象都是从某个新memory pool中分配的了。
这里我们利用了以下几个特性:
1. 模板特性,它为每个不同模板参数的声明生成一个新类,并copy模板类中所有的东西。
2. class域,在class域中,我们定义的static成员,隶属于所有的类。
3. 函数内的static变量:只初始化一次,并且在这个函数被调用时初始化。
问题:函数内的static变量初始化是不是线程安全的呢?这个问题我将在另外一篇文章中解释。不过我想你应该懂的,看了我的实现之后。