Chinaunix首页 | 论坛 | 博客
  • 博客访问: 367476
  • 博文数量: 38
  • 博客积分: 256
  • 博客等级: 入伍新兵
  • 技术积分: 846
  • 用 户 组: 普通用户
  • 注册时间: 2012-12-14 23:21
文章分类

全部博文(38)

文章存档

2015年(1)

2014年(1)

2013年(28)

2012年(8)

我的朋友

分类: C/C++

2013-01-21 22:21:45

概述:

一般来说,我们不会对全局的new/delete操作符进行重载,而是对某个特定的class。这样我们不会改变软件域中,开发人员和维护人员对new和delete的理解。

在这篇文章中,我将会用演绎的方式来阐述一种设计模式,用于提供功能性的复用,我们叫它“功能模式”。

基本的情境是这样的:在一个软件系统中,我们可以构建一个叫memory
pool的container,每个这样的container可以为我们分配一个固定大小的内存块,以此来加快内存的分配。因为每个memory
pool是为某个特定的一个类或者一组类(具有继承关系)服务的,这样也会减少分配这些对象是的竞争情况(现在没有那个软件还是单线程的吧??)。

Round-1:

我们要做到概述中描述的情境,必须做以下几件事情:

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需要这么做,你不是产生了一大堆的重复代码?如果你回答我说,我不在乎,只要能工作就行,管它呢。那么我劝你早点转行,或者至少不需要再浪费时间看这篇文章了。


Round-2


为了减少这样的重复代码,我们可能会想到用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还没被初始化,将会发生什么事情?答案是:什么事情都有可能,最不好的就是似乎什么都没发生。


那么我们有没有更好的办法来处理这个事情呢?


Round-3


先来看看代码:


template


class 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变量初始化是不是线程安全的呢?这个问题我将在另外一篇文章中解释。不过我想你应该懂的,看了我的实现之后。

阅读(1701) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~