上一章完成的兩個 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 的雛形。操作系統