- 首先有个能创建固定长度的内存块的池
/// <summary>
/// 申请固定大小的内存池
/// 内部使用类,不考虑那些安全隐患的地方
/// 比如不用考虑归还的内存是否长度对应等
/// </summary>
class MyDogPoolFixSize
{
uint16 mNum;
int32 fixsize;
void* mPool;
public:
/// <summary>
/// fixs为固定内存大小
/// 最小为sizeof(void*) + 1
/// </summary>
MyDogPoolFixSize(int32 fixs);
~MyDogPoolFixSize();
/// <summary>
/// 创建cnt个内存放入池里
/// </summary>
void create(int32 cnt = 1);
/// <summary>
/// 申请内存
/// </summary>
vptr apply();
/// <summary>
/// 回收内存
/// </summary>
void free(vptr data);
/// <summary>
/// releasePro是每次release时的释放比例(百分比)
/// </summary>
void release(int8 releasePro = 60);
};
在这个基础上,对这些池进行管理,于是就有了
#pragma pack(push)
#pragma pack(1)
struct PoolConfItem {
uint32 fixsize;//用于MyDogPoolFixSize初始化
uint8 releasePro;//每次release时的释放比例(百分比)
uint16 releaseCD;//释放cd 秒
};
#pragma pack(pop)
struct BuffItem {
vptr data;
uint8 index;//max_uint8表示超出配置
BuffItem():data(null),index(max_uint8){}
BuffItem(vptr d,int i):data(d),index(i){}
~BuffItem() { data = nullptr; }
};
class ReadBuff;
/// <summary>
/// 管理所有的MyDogPoolFixSize
/// 由于主要服务于属性集,所以可以在编译前知道各种属性集大小,从而得出PoolConfItem
/// </summary>
class MyDogPoolMgr {
PoolConfItem* pool_items;
MyDogPoolFixSize** mPool;
uint8 mLeng;
public:
MyDogPoolMgr(ReadBuff& rb);
~MyDogPoolMgr();
/// <summary>
/// 根据大小,获取index
/// </summary>
uint8 s2index(uint32 s, uint32* rs=null);
/// <summary>
/// 获取配置最大index
/// </summary>
inline uint8 maxindex() {
return mLeng - 1;
}
/// <summary>
/// 根据index申请一段内存
/// </summary>
BuffItem applyByid(uint8 index);
/// <summary>
/// 根据大小申请一段内存
/// </summary>
BuffItem applyBysize(uint32 s);
/// <summary>
/// 归还内存
/// </summary>
void replay(BuffItem& item);
};
这样设计,关键还是这句话:
由于主要服务于属性集,所以可以在编译前知道各种属性集大小,从而得出PoolConfItem
配置内容,可结合TCMalloc小内存的参数,以及由工具分析出来的配置
s2index函数是很有用的,rs返回真实内存块大小
- 下面的类就是对这个pool的直接使用
/// <summary>
/// 非连续内存的dispbuff
/// </summary>
class WriteDispBuff {
vector<BuffItem> vec;
uint8 mApplyid;//内存不够时,固定申请
uint32 mFixsize;//每个块的实际大小
uint32 mLeft;//当前写入块剩余长度
dptr getdata();
public:
WriteDispBuff(uint32 s = 128);
~WriteDispBuff();
/// <summary>
/// 写入内存
/// </summary>
template<typename T> WriteDispBuff& operator<<(const T& t) {
int32 s = sizeof(T);
uint32 off = 0;
while (s>0) {
dptr rdata = getdata();
if (mLeft >= s) {
memcpy(rdata, (dptr)(&t) + off, s);
mLeft -= s;
break;
}
else {
memcpy(rdata,(dptr)(&t) + off, mLeft);
mLeft = 0;
off += mLeft;
s -= mLeft;
}
}
return *this;
}
/// <summary>
/// 写入内存
/// </summary>
WriteDispBuff& writeBuff(dptr data,uint32 s);
/// <summary>
/// fixs每块的大小
/// useBlock用了几块
/// left最后一块的使用长度
/// </summary>
inline void getInfo(uint32& fixs, uint16& useBlock, uint32& leng) {
fixs = mFixsize;
useBlock = vec.size();
leng = mFixsize - mLeft;
}
/// <summary>
/// 读取内存,和getInfo配合使用
/// </summary>
inline dptr readData(uint16 index) {
return (dptr)vec[index].data;
}
};
具体的cpp看源码吧...
- 非连续内存的dispbuff,这个也是很有用的东西,它不需要知道要写入的数据有多大.序列化数据就很管用.另外可以通过getInfo和readData接口遍历数据.