CG@CPPBLOG

/*=========================================*/
随笔 - 76, 文章 - 39, 评论 - 137, 引用 - 0
数据加载中……

《C++设计新思维》读书笔记(23)

4.4 Chunks(大块内存)

每个Chunk对象包含并管理一大块内存,其中包含固定数量的区块。你可以在构造期间设定区块的大小和数量。你可以从chunk中分配和归还区块。一旦chunk之中没有剩余区块,分配函数便传回0chunk定义如下:

// Nothing is private - Chunk is a Plain Old Data (POD) structure
// structure defined inside FixedAllocator
// and manipulated only by it
struct Chunk{
    
void Init(std::size_t blockSize, unsigned char blocks);
    
void Release();
    
void* Allocate(std::size_t blockSize);
    
void Deallocate(void* p, std::size_t blockSize);
    unsigned 
char* pData_;
    unsigned 
char
        firstAvailableBlock_,
        blocksAvailable_;
};

除了一个指针指向被管理内存本身,firstAvailableBlock_保存chunk内第一个可用区块的索引,blocksAvailable_保存chunk内可用区块总数。

Chunk的接口非常简单。Init()用于初始化,Release()用来释放。Allocate()用来分配,Deallocate()用来归还。Chunk不保存区块的大小,它没有构造函数、析构函数和赋值运算符,定义自己的copy语义会损及上一层的效率——上一层将chunk置于一个vector

chunk的结构反应出设计中一个重要折衷。blocksAvailable_firstAvailableBlock_都是unsigned char型别,因此一个chunk无法拥有255个以上的区块。

另外我们利用未被使用的区块的第一个bytes来存放下一个未被使用的区块的索引号。这样我们就拥有了一个单向链表。无需占用内存。

初始化函数如下:

void Chunk::Init(std::size_t blockSize, unsigned char blocks){
    pData_ 
= new unsigned char[blockSize * blocks];
    firstAvailableBlock_ 
= 0;
    blocksAvailable_     
= blcoks;
    unsigned 
char i      = 0;
    unsigned 
char* p     = pData_;
    
for(; i != blocks; p += blockSize){
        
*= ++i;
    }
}

我们来看一下将区块数量限制在unsigned char的大小(0~255)的原因。假如我们使用一个较大型别,比如unsigned short0~65535),我们将遭遇两个问题,一个小问题,一个大问题。

小问题是我们无法分配小于sizeof(unsigned short)的区块,这令人尴尬,因为我们正在建立一个小型对象分配器。(这里指的是,对于1 byte区块大小的chunk,由于索引号有2 bytes,无法建立内嵌的单向链表,cuigang)。

大问题是齐位(alignment)问题。假设你为一个5 bytes区块建立一个专属分配器。这种情况下如果想将指向如此一个5 bytes区块的指针转换为unsigned int(原文如此,估计作者是想说从一个起始为奇地址的区块提领(dereference)一个偶字节变量会导致异常的问题,这几个函数中都有提领的动作。cuigang),会造成不确定行为。

unsigned char类型可以简单的解决这个问题。它大小为1,无齐位问题。只不过我们将无法分配多于255的区块。但这个我们是可以接受的。

分配函数Allocate()是典型的list操作。

void* Chunk::Allocate(std::size_t blockSize){
    
if(!blocksAvailable_) return 0;
    unsigned 
char* pResult =
        pData_ 
+ (firstAvailableBlock_ * blockSize);
    firstAvailableBlock_ 
= *pResult;
    
--blocksAvailable_;
    
return pResult;
};

这个函数成本很小,不需要查找动作,在常数事件内完成,而不像系统分配需要线性时间。

归还函数Deallocate()行为相反,这里要注意,由于Chunk对区块大小一无所知,所以你必须将区块大小当作参数传入,同时注意里面很多的异常处理来避免你将错误指针传入:

void Chunk::Deallocate(void* p, std::size_t blockSize){
    assert(p 
>= pData_);
    unsigned 
char* toRelease = static_cast<unsigned char*>(p);
    assert((toRelease 
- pData_) % blockSize == 0);
    
*toRelease = firstAvailableBlock_;
    firstAvailableBlcok_ 
= static_cast<unsigned char>(
        (toRelease 
- pData_) / blockSize);
    assert(firstAvailableBlock 
== (toRelease - pData_) / blockSize);
    
++blocksAvailable_;
};


posted on 2008-01-12 20:29 cuigang 阅读(622) 评论(0)  编辑 收藏 引用 所属分类: 《C++设计新思维》读书笔记


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理