到现在看来,简单的空闲链表的主要开销就是一个next指针域,在32位系统上是4个字节。可是如果我们的CMyObject本来就很小,只有4个字节,那额外开销可就是100%阿,如果系统中用到几万,十几万,甚至上百万个CMyObject,就不容小视了!
我们仔细来分析一下刚才的例子,什么时候会用到next指针呢?
1. 第一次分配对象,建立整个freeStore的链表的时候,初始化各个实例的next指针
2. 每次给申请者一个实例时,要修改freeStore = freeStore->next
3. 每次归还一个实例时,链表插入操作要用到next.
这里我们的一个观察是,next指针的使用都是在申请和归还的时候,也就是说申请者在使用这块内存的时候从来都不需要next!既然next指针所占的4个字节不会被同时使用,那我们是不是可以想一个办法,让使用时和申请/归还重用同一块内存呢。感谢C语言的union,给了我们实现的可能性。
class CMyObject
{
private:
struct CMyObjectRep{
int _data1;
char _data2;
};
private:
//注意,这里是个匿名的union,我们把使用期的成员变量都放在一个结构里面
union{
CMyObjectRep rep; // 使用期会用到的成员变量
CMyObject* next; // 分配,归还时使用的,服务于空闲链表操作
};
public:
//其他都和简单的空闲链表的实现一模一样
};
void* CMyObject::operator new(size_t size)的实现也和上一篇中提到的实现一模一样,这里就不重复了。
我们仔细来分析一下刚才的例子,什么时候会用到next指针呢?
1. 第一次分配对象,建立整个freeStore的链表的时候,初始化各个实例的next指针
2. 每次给申请者一个实例时,要修改freeStore = freeStore->next
3. 每次归还一个实例时,链表插入操作要用到next.
这里我们的一个观察是,next指针的使用都是在申请和归还的时候,也就是说申请者在使用这块内存的时候从来都不需要next!既然next指针所占的4个字节不会被同时使用,那我们是不是可以想一个办法,让使用时和申请/归还重用同一块内存呢。感谢C语言的union,给了我们实现的可能性。
class CMyObject
{
private:
struct CMyObjectRep{
int _data1;
char _data2;
};
private:
//注意,这里是个匿名的union,我们把使用期的成员变量都放在一个结构里面
union{
CMyObjectRep rep; // 使用期会用到的成员变量
CMyObject* next; // 分配,归还时使用的,服务于空闲链表操作
};
public:
//其他都和简单的空闲链表的实现一模一样
};
void* CMyObject::operator new(size_t size)的实现也和上一篇中提到的实现一模一样,这里就不重复了。
看来现在的实现不错了哦-:)分配性能也提高了,也没有额外的开销,但是问题是,如果现在有了一个新的叫做CYourObject的类,他也想使用这样的分配策略,我们就要为CYourObject重写刚才所有的代码,以后又有CHisObject,CHerObject呢…?!怎么办?我们得把这些代码抽出来,给大家用!我们来考虑一下,我初步的打算是写一个CAlloc的类,我们来分析一下需求:
1.这个类至少知道实例的大小,例如sizeof(CMyObject);我们可以使用模板,也可以由参数输入
2.链表的初始化长度(这个可以系统预设,也可以由参数输入)
3.这个类是服务于类而不是服务于实例,所以应该是被服务类的静态成员变量。
1.这个类至少知道实例的大小,例如sizeof(CMyObject);我们可以使用模板,也可以由参数输入
2.链表的初始化长度(这个可以系统预设,也可以由参数输入)
3.这个类是服务于类而不是服务于实例,所以应该是被服务类的静态成员变量。
template <class T>
class CAlloc{
public:
// 分配大小为n的内存,一般n应该等于sizeof(T)
T* allocate(size_t n);
class CAlloc{
public:
// 分配大小为n的内存,一般n应该等于sizeof(T)
T* allocate(size_t n);
// 归还大小为n的内存
void deallocate(T* p,size_t n);
public:
// 初始化空闲链表的大小
CAlloc(size_t n = 512);
// 释放CAlloc<T>中维护的所有内存
~ CAlloc();
};
void deallocate(T* p,size_t n);
public:
// 初始化空闲链表的大小
CAlloc(size_t n = 512);
// 释放CAlloc<T>中维护的所有内存
~ CAlloc();
};
class CMyObject
{
private:
static CAlloc<CMyObject> memPool;
};
{
private:
static CAlloc<CMyObject> memPool;
};
CAlloc<CMyObject> CMyObject::memPool(512);
inline void* CMyObject::operator new(size_t size)
{
return memPool.allocate(size);
}
{
return memPool.allocate(size);
}
inline void CMyObject::operator delete(void* p, size_t size)
{
memPool.deallocate(p,size);
}
是不是干净很多呢,实际上还可以使用宏展开把那些个 声明和定义都简化了。MFC就是这么干的,我也来学一把,哈哈:
#define DECLARE_ALLOC(class_name)
public:
void* operator new(size_t size) { return memPool.allocate(size); }
void operatoe delete(void* p, size_t size) { return memPool.deallocate(p,size); }
private:
static CAlloc<class_name> memPool;
{
memPool.deallocate(p,size);
}
是不是干净很多呢,实际上还可以使用宏展开把那些个 声明和定义都简化了。MFC就是这么干的,我也来学一把,哈哈:
#define DECLARE_ALLOC(class_name)
public:
void* operator new(size_t size) { return memPool.allocate(size); }
void operatoe delete(void* p, size_t size) { return memPool.deallocate(p,size); }
private:
static CAlloc<class_name> memPool;
#define IMPLEMENT_ALLOC(class_name, block_size)
CAlloc<class_name> class_name::memPool(block_size);
CAlloc<class_name> class_name::memPool(block_size);
嘿嘿,现在我们来写写CYourObject -:)
class CYourObject{
DECLARE_ALLOC(CYourObject)
public:
};
IMPLEMENT_ALLOC(CYourObject,512)
恩,现在就差不多了~~ 不过还有更精巧的,下一次我们看真正的SGI的内存分配实现~~
class CYourObject{
DECLARE_ALLOC(CYourObject)
public:
};
IMPLEMENT_ALLOC(CYourObject,512)
恩,现在就差不多了~~ 不过还有更精巧的,下一次我们看真正的SGI的内存分配实现~~