乐趣区

关于c++:C内存管理6static-allocator

上一章实现的两个 allocator 必须为不同的 classes 重写一遍简直雷同的 member operator new 和 member operator delete,应该有办法将一个总是调配特定尺寸块的 memory allocator 概念包装起来,使它容易被重复使用。
以下展现一种形式,每个 allocator object 都是个分配器,它保护一个 free-list;不同的 allocator objects 保护不同的 free-list。

#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;
}

输入:[留神察看每五次输入之间,地址的连续性]

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 正确工作。

退出移动版