上一章完成的两个 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; }
输出:[注意观察每五次输出之间,地址的连续性]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
#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)
将前述 allocator 进一步发展为具有 16 条 free-list, 并所以再也不以 application classes 内的static 呈现,而是一个 global allocator -- 这就是 G2.9 的 std::alloc 的雏形。操作系统