上一章实现的两个 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) = 40xeb80e8 00xeb80ec 10xeb80f0 20xeb80f4 30xeb80f8 40xeb8108 50xeb810c 60xeb8110 70xeb8114 80xeb8118 90xeb8128 100xeb812c 110xeb8130 120xeb8134 130xeb8138 140xeb8148 150xeb814c 160xeb8150 170xeb8154 180xeb8158 190xeb8168 200xeb816c 210xeb8170 22============sizeof(Foo) = 400xeb8188 (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 正确工作。