【C++内存管理】6_static allocator

上一章完成的两个 allocator 必须为不一样的 classes 重写一遍几乎相同的 member operator new 和 member operator delete,应该有方法将一个老是分配特定尺寸块的 memory allocator 概念包装起来,使它容易被重复使用。
如下展现一种方式,每一个 allocator object 都是个分配器,它维护一个 free-list;不一样的 allocator objects 维护不一样的 free-list。前端

static allocator

#include <iostream>
#include <cstdlib>
#include <string>
#include <complex>

using std::string;
using std::complex;
using std::cout;
using std::endl;

class allocator
{
private:
    struct obj {
        struct obj *next; // embedded pointer
    };

public:
    void *allocate(size_t);
    void deallocate(void*, size_t);

private:
    obj *freeStore = nullptr;
    const int CHUNK = 5;  // 小一些方便观察
};

// 操做系统会尽可能保证每次申请 CHUNK * size 内存时是连续的, 但两次之间无此承诺
void *allocator::allocate(size_t size)
{
    obj *p;

    if (!freeStore)
    {
        // linked list 为空,因而申请一大块内存
        size_t chunk = CHUNK * size;
        freeStore = p = static_cast<obj*>(malloc(chunk));

        // 将分配得来的一大块看成 linked list 般,小块小块串起来
        for (int i=0; i<(CHUNK-1); ++i)
        {
            p->next = reinterpret_cast<obj*>((char*)p + size);
            p = p->next;
        }
    }

    p = freeStore;
    freeStore = freeStore->next;

    return p;
}

void allocator::deallocate(void* p, size_t)
{
    // 将 *p 收回插入 free list 前端
    static_cast<obj*>(p)->next = freeStore;
    freeStore = static_cast<obj*>(p);
}

//=========================

class Foo {
public:
    long L;
    static allocator myAlloc;

public:
    Foo(long l) : L(l)
    { }
    static void *operator new(size_t size)
    {
        return myAlloc.allocate(size);
    }

    static void operator delete(void *pdead, size_t size)
    {
        return myAlloc.deallocate(pdead, size);
    }
};

allocator Foo::myAlloc;

class Goo {
public:
    complex<double> c;
    string str;
    static allocator myAlloc;

public:
    Goo(const complex<double> &x) : c(x)
    { }

    static void *operator new(size_t size)
    {
        return myAlloc.allocate(size);
    }

    static void operator delete(void *pdead, size_t size)
    {
        return myAlloc.deallocate(pdead, size);
    }
};

allocator Goo::myAlloc;

void func_1()
{
    Foo *p[100];

    cout << "sizeof(Foo) = " << sizeof(Foo) << endl;

    for (size_t i=0; i<23; ++i)
    {
        if (i != 0 && i % 5 == 0)
            cout << endl;

        p[i] = new Foo(i);
        cout << p[i] << ' ' << p[i]->L << endl;
    }

    for (int i=0; i<23; ++i)
        delete  p[i];
}

void func_2()
{
    Goo *p[100];

    cout << "sizeof(Foo) = " << sizeof(Goo) << endl;

    for (size_t i=0; i<17; ++i)
    {
        if (i != 0 && i % 5 == 0)
            cout << endl;

        p[i] = new Goo(complex<double>(i, i));
        cout << p[i] << ' ' << p[i]->c << endl;
    }

    for (int i=0; i<17; ++i)
        delete  p[i];
}

int main()
{
    func_1();

    cout << "============" << endl;

    func_2();

    return 0;
}

输出:[注意观察每五次输出之间,地址的连续性]ios

sizeof(Foo) = 4
0xeb80e8 0
0xeb80ec 1
0xeb80f0 2
0xeb80f4 3
0xeb80f8 4

0xeb8108 5
0xeb810c 6
0xeb8110 7
0xeb8114 8
0xeb8118 9

0xeb8128 10
0xeb812c 11
0xeb8130 12
0xeb8134 13
0xeb8138 14

0xeb8148 15
0xeb814c 16
0xeb8150 17
0xeb8154 18
0xeb8158 19

0xeb8168 20
0xeb816c 21
0xeb8170 22
============
sizeof(Foo) = 40
0xeb8188 (0,0)
0xeb81b0 (1,1)
0xeb81d8 (2,2)
0xeb8200 (3,3)
0xeb8228 (4,4)

0xeb8258 (5,5)
0xeb8280 (6,6)
0xeb82a8 (7,7)
0xeb82d0 (8,8)
0xeb82f8 (9,9)

0xeb8408 (10,10)
0xeb8430 (11,11)
0xeb8458 (12,12)
0xeb8480 (13,13)
0xeb84a8 (14,14)

0xeb84d8 (15,15)
0xeb8500 (16,16)

这比先前的设计干净多了, application classed 再也不与内存分配细节纠缠不清,全部相关细节都让 allocator 去操心,咱们的工做是让 application classes 正确工做。app

macro for static allocator

#include <iostream>
#include <cstdlib>
#include <string>
#include <complex>

using std::string;
using std::complex;
using std::cout;
using std::endl;

class allocator
{
private:
    struct obj {
        struct obj *next; // embedded pointer
    };

public:
    void *allocate(size_t);
    void deallocate(void*, size_t);

private:
    obj *freeStore = nullptr;
    const int CHUNK = 5;  // 小一些方便观察
};

// 操做系统会尽可能保证每次申请 CHUNK * size 内存时是连续的, 但两次之间无此承诺
void *allocator::allocate(size_t size)
{
    obj *p;

    if (!freeStore)
    {
        // linked list 为空,因而申请一大块内存
        size_t chunk = CHUNK * size;
        freeStore = p = static_cast<obj*>(malloc(chunk));

        // 将分配得来的一大块看成 linked list 般,小块小块串起来
        for (int i=0; i<(CHUNK-1); ++i)
        {
            p->next = reinterpret_cast<obj*>((char*)p + size);
            p = p->next;
        }
    }

    p = freeStore;
    freeStore = freeStore->next;

    return p;
}

void allocator::deallocate(void* p, size_t)
{
    // 将 *p 收回插入 free list 前端
    static_cast<obj*>(p)->next = freeStore;
    freeStore = static_cast<obj*>(p);
}

// DECLARE_POOL_ALLOC -- used in class definition
#define DECLARE_POLL_ALLOC()                    \
public:                                         \
void *operator new(size_t size)                 \
{                                               \
    return myAlloc.allocate(size);              \
}                                               \
void operator delete(void *pdead, size_t size)  \
{                                               \
    return myAlloc.deallocate(pdead, size);     \
}                                               \
protected:                                      \
    static allocator myAlloc;

// IMPLEMENT_POOL_ALLOC -- used in class implementcation file
#define IMPLEMENT_POOL_ALLOC(class_name)        \
    allocator class_name::myAlloc;

//=========================

class Foo {
    DECLARE_POLL_ALLOC();
public:
    long L;

public:
    Foo(long l) : L(l)
    { }
};

IMPLEMENT_POOL_ALLOC(Foo);

class Goo {
    DECLARE_POLL_ALLOC();
public:
    complex<double> c;
    string str;

public:
    Goo(const complex<double> &x) : c(x)
    { }
};

IMPLEMENT_POOL_ALLOC(Goo);

void func_1()
{
    Foo *p[100];

    cout << "sizeof(Foo) = " << sizeof(Foo) << endl;

    for (size_t i=0; i<23; ++i)
    {
        if (i != 0 && i % 5 == 0)
            cout << endl;

        p[i] = new Foo(i);
        cout << p[i] << ' ' << p[i]->L << endl;
    }

    for (int i=0; i<23; ++i)
        delete  p[i];
}

void func_2()
{
    Goo *p[100];

    cout << "sizeof(Foo) = " << sizeof(Goo) << endl;

    for (size_t i=0; i<17; ++i)
    {
        if (i != 0 && i % 5 == 0)
            cout << endl;

        p[i] = new Goo(complex<double>(i, i));
        cout << p[i] << ' ' << p[i]->c << endl;
    }

    for (int i=0; i<17; ++i)
        delete  p[i];
}

int main()
{
    Foo *pF[100];
    Goo *pG[100];

    cout << "sizeof(Foo)=" << sizeof(Foo) << endl;
    cout << "sizeof(Goo)=" << sizeof(Goo) << endl;

    for (int i=0; i<23; ++i)
    {
        pF[i] = new Foo(i);
        pG[i] = new Goo(i);

        cout << pF[i] << ' ' << pF[i]->L << "\t";
        cout << pG[i] << ' ' << pG[i]->c << "\n";
    }

    for (int i=0; i<23; ++i)
    {
        delete pF[i];
        delete pG[i];
    }

    return 0;
}

输出:spa

sizeof(Foo)=4
sizeof(Goo)=40
0x7a80e8 0    0x7a8108 (0,0)
0x7a80ec 1    0x7a8130 (1,0)
0x7a80f0 2    0x7a8158 (2,0)
0x7a80f4 3    0x7a8180 (3,0)
0x7a80f8 4    0x7a81a8 (4,0)
0x7a81d8 5    0x7a8398 (5,0)
0x7a81dc 6    0x7a83c0 (6,0)
0x7a81e0 7    0x7a83e8 (7,0)
0x7a81e4 8    0x7a8410 (8,0)
0x7a81e8 9    0x7a8438 (9,0)
0x7a82d8 10    0x7a8468 (10,0)
0x7a82dc 11    0x7a8490 (11,0)
0x7a82e0 12    0x7a84b8 (12,0)
0x7a82e4 13    0x7a84e0 (13,0)
0x7a82e8 14    0x7a8508 (14,0)
0x7a82f8 15    0x7a8538 (15,0)
0x7a82fc 16    0x7a8560 (16,0)
0x7a8300 17    0x7a8588 (17,0)
0x7a8304 18    0x7a85b0 (18,0)
0x7a8308 19    0x7a85d8 (19,0)
0x7a8318 20    0x7a8608 (20,0)
0x7a831c 21    0x7a8630 (21,0)
0x7a8320 22    0x7a8658 (22,0)

global allocator (with multiple free-lists)

将前述 allocator 进一步发展为具有 16 条 free-list, 并所以再也不以 application classes 内的static 呈现,而是一个 global allocator -- 这就是 G2.9 的 std::alloc 的雏形。
image.png操作系统

相关文章
相关标签/搜索