第四章 小型對象分配技術(shù)

  • 書上的內(nèi)容基本上在下面的代碼討論了,但是并沒有完全按照書上實現(xiàn)
#include <cassert>
#include <vector>
#include <map>


using std::vector;
using std::map;


/*
管理相同自定義大小的內(nèi)存塊,可以使用的內(nèi)存總數(shù)的限制在于:
1.系統(tǒng)(安裝的內(nèi)存大小)
2.程序類型(32位、64位)
*/
class CFixedAllocator
{
public:
    CFixedAllocator(const size_t nBlockSizeC, 
        const unsigned char nBlockCountC) : nBlockSize_(nBlockSizeC), 
        nBlockCount_(nBlockCountC), pAlloc_(nullptr), pDealloc_(nullptr)
    {
        assert(nBlockCountC);
    }

private:
    //管理相同自定義大小的內(nèi)存塊,內(nèi)存塊總數(shù)不超過255個
    struct SChunkMemory
    {
        const bool Init(const size_t nBlockSizeC,
            const unsigned char nBlockCountC);
        void Release();
        void* Allocate(const size_t nBlockSizeC);
        void Deallocate(void* pValue, const size_t nBlockSizeC);

        unsigned char* pData_;                  //分配的內(nèi)存
        unsigned char nFirstAvailableBlock_;    //第一個可用的區(qū)塊索引
        unsigned char nBlockAvailableCount_;    //可用區(qū)塊總數(shù)
        /*
        這里的索引與總數(shù)都是單字節(jié)類型是為了兼容只分配一個字節(jié)的情況
        */
    };

private:
    size_t nBlockSize_;
    unsigned char nBlockCount_;
    vector<SChunkMemory> vecChunkMemory_;
    SChunkMemory* pAlloc_;
    SChunkMemory* pDealloc_;
/*
為了提高查找速度,面對每一次分配,并非遍歷整個vecChunkMemory_來尋找空間,
而是保存一個指針,指向最近一次分配所使用的SChunkMemory,若其不存在可用空間,
才會引發(fā)一次線性查找
*/

public:
    void* Allocate();
    const bool Deallocate(void* pValue);
    const size_t GetBlockSize() const { return nBlockSize_; }
    void Release();
};

const bool CFixedAllocator::SChunkMemory::Init(const size_t nBlockSizeC,
    const unsigned char nBlockCountC)
{
    pData_ = new (std::nothrow) unsigned char[nBlockSizeC * nBlockCountC];
    if (!pData_)
    {
        return false;
    }

    nFirstAvailableBlock_ = 0;
    nBlockAvailableCount_ = nBlockCountC;

    unsigned char* pTem = pData_;
    for (int i = 0; i < nBlockCountC; pTem += nBlockSizeC)
    {
        *pTem = ++i;
        /*
        拿未被使用的區(qū)塊的第一個字節(jié)來放置下一個未被使用的區(qū)塊的索引,
        這樣就有了一個由可用區(qū)塊組成的單向鏈表,且無需占用額外內(nèi)存
        */
    }

    return true;
}

void CFixedAllocator::SChunkMemory::Release()
{
    if (pData_)
    {
        delete[] pData_;
    }
    nBlockAvailableCount_ = 0;
}

void* CFixedAllocator::SChunkMemory::Allocate(const size_t nBlockSizeC)
{
    if (!nBlockAvailableCount_)
    {
        return nullptr;
    }

    unsigned char* pResult = pData_ + nFirstAvailableBlock_ * nBlockSizeC;
    nFirstAvailableBlock_ = *pResult;
    --nBlockAvailableCount_;
    return pResult;
}

void CFixedAllocator::SChunkMemory::Deallocate(void* pValue, 
    const size_t nBlockSizeC)
{
    unsigned char* pRelease = static_cast<unsigned char*>(pValue);

    assert(pValue >= pData_);
    assert(!((pRelease - pData_) % nBlockSizeC));

    //指明下一個可用內(nèi)存塊Id,道理其實和Init里是一置的
    *pRelease = nFirstAvailableBlock_; 
    nFirstAvailableBlock_ = 
        static_cast<unsigned char>((pRelease - pData_) / nBlockSizeC);
    assert(nFirstAvailableBlock_ == (pRelease - pData_) / nBlockSizeC);

    ++nBlockAvailableCount_;
}

void* CFixedAllocator::Allocate()
{
    if (!pAlloc_ || !pAlloc_->nBlockAvailableCount_)
    {
        for (auto it = vecChunkMemory_.begin(); ; ++it)
        {
            if (vecChunkMemory_.end() == it)
            {
                vecChunkMemory_.emplace_back(SChunkMemory());
                if (!vecChunkMemory_.back().Init(nBlockSize_,
                    nBlockCount_))
                {
                    return nullptr;
                }

                pAlloc_ = &vecChunkMemory_.back();
                pDealloc_ = &vecChunkMemory_.front();
                break;
            }
            else if (it->nBlockAvailableCount_)
            {
                pAlloc_ = &*it;
                break;
            }
        }
    }

    return pAlloc_->Allocate(nBlockSize_);
}

const bool CFixedAllocator::Deallocate(void* pValue)
{
/*
策略一:可以專門建立一個vector來保存歸還的內(nèi)存塊,當客戶進行內(nèi)存歸還的時候,
就將此內(nèi)存添加到vector中,用戶再申請的時候就直接從這個vector中取。
以上這個策略在用戶隨機分配和歸還時候效果好,但是在用戶批量分配和批量釋放時候,
效果很差,因為其需要維護一個額外的vector

策略二:建立一個指針指向上次進行歸還的那個SChunkMemory對象,
如果被歸還的指針不屬于這個對象,則進行線性查找
*/
    if (pValue >= pDealloc_->pData_ && 
        pValue <= pDealloc_->pData_ + nBlockSize_ * nBlockCount_)
    {
        pDealloc_->Deallocate(pValue, nBlockSize_);
        return true;
    }

    for (auto it = vecChunkMemory_.begin(); it != vecChunkMemory_.end(); 
        ++it)
    {
        if (pValue >= it->pData_ && 
            pValue <= it->pData_ + nBlockSize_ * nBlockCount_)
        {
            it->Deallocate(pValue, nBlockSize_);
            return true;
        }
    }

    return false;
}

void CFixedAllocator::Release()
{
    for (auto it = vecChunkMemory_.begin();
        it != vecChunkMemory_.end(); ++it)
    {
        it->Release();
    }
    vecChunkMemory_.clear();
}


//自己編的  其分配的內(nèi)存大小可以自定義
class CSmallObjAllocator
{
public:
    CSmallObjAllocator(const unsigned char nBlockCountC = 255) : 
      nBlockCountC_(nBlockCountC) {}

public:
    void* Allocate(const size_t nMemorySizeC);
    const bool  Deallocate(void* pValue, const size_t nMemorySizeC);
    void Release();

private:
    map<size_t, CFixedAllocator> mapFixAllocator_;
    const unsigned nBlockCountC_;
};

void* CSmallObjAllocator::Allocate(const size_t nMemorySizeC)
{
    auto it = mapFixAllocator_.find(nMemorySizeC);

    if (it != mapFixAllocator_.end())
    {
        return it->second.Allocate();
    }

    mapFixAllocator_.emplace(std::make_pair(nMemorySizeC, 
        CFixedAllocator(nMemorySizeC, nBlockCountC_)));
    return mapFixAllocator_.at(nMemorySizeC).Allocate();
}

const bool CSmallObjAllocator::Deallocate(void* pValue, 
    const size_t nMemorySizeC)
{
    auto it = mapFixAllocator_.find(nMemorySizeC);

    if (it == mapFixAllocator_.end())
    {
        return false;
    }

    return it->second.Deallocate(pValue);
}

void CSmallObjAllocator::Release()
{
    for (auto it = mapFixAllocator_.begin();
        it != mapFixAllocator_.end(); ++it)
    {
        it->second.Release();
    }

    mapFixAllocator_.clear();
}


int main()
{
    CSmallObjAllocator SmallObj;
    void* aPValue[] = 
    {
        SmallObj.Allocate(10), 
        SmallObj.Allocate(10),
        SmallObj.Allocate(1),
        SmallObj.Allocate(2), 
        SmallObj.Allocate(5)
    };

    SmallObj.Deallocate(aPValue[0], 10);
    SmallObj.Deallocate(aPValue[1], 10);
    SmallObj.Deallocate(aPValue[2], 1);
    SmallObj.Deallocate(aPValue[3], 2);
    SmallObj.Deallocate(aPValue[4], 5);

    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容