背景
在面向對象類的設計中,有時為了強化效能,特別是當構造大量小對象時,為了改善內存碎片,就需要自己實現對象的內存管理,以替換系統缺省的分配和釋放行為,即全局的new和delete。按照c++標准,在定制類專屬的new和delete時,為了減免客戶代碼使用時的麻煩和問題,需要考慮同時定制簡單(normal new)、定位(placement new)和無異常(nothrow new)三種new情形,以及與之配對的三種delete情形,另外還有對應的數組new[]和delete[]各三種情形。在接口設計上,每種情形都是operator new和operator delete的重載版本;在內存管理上,具體的對象空間分配和釋放是靈活的,因此這一部分可實現為策略模式,通過改變替換不同的內存管理策略,即可輕易獲得不同的內存分配和釋放行為,而類的代碼則無須改變。為了方便定制類的new和delete,可以從一個接口基類模板繼承而自動獲得這種特性。這個基類模板實現了單個對象的new、delete和對象數組的new、delete,而模板參數正是內存管理策略類,它的設計約束如下:
1)必須存在static成員方法malloc和free,其參數和返回值與C庫的malloc和free一致。
2)malloc只分配空間,若分配成功則不必初始化,否則失敗返回NULL,不能拋出異常,因為normal new的語義為對於分配失敗則拋出std::bad_alloc異常,而nothrow new則返回NULL,如此兩種方式兼備,有利於客戶代碼的按需靈活檢測;free只釋放或歸還空間。
3)malloc和free的內部實現是靈活的,由應用開發者定制。
組件
這裡實現了new_delete_policy_base和object_pool_impl兩個基礎組件,代碼如下,前者是支持內存管理策略的定制new和delete接口基類模板,從該類繼承的子類其對象的構造和析構就被定制了;後者是支持內存管理策略的非侵入式對象池類模板,可直接用於構造某類的對象,包括內建的基本數據類型,而該類不必從new_delete_policy_base繼承。
1template<class Alloc>
2class new_delete_policy_base
3{
4public:
5 static void* operator new(size_t size) throw (std::bad_alloc)
6 {
7 void* ptr = Alloc::malloc(size);
8 if(NULL==ptr) {
9 throw std::bad_alloc();
10 }
11 return ptr;
12 }
13
14 static void* operator new(size_t size,void* ptr) throw()
15 { return ptr; }
16
17 static void* operator new(size_t size,const std::nothrow_t&) throw()
18 { return Alloc::malloc(size); }
19
20 static void operator delete(void* ptr) throw()
21 { Alloc::free(ptr); }
22
23 static void operator delete(void* ptr, const std::nothrow_t&) throw()
24 { Alloc::free(ptr); }
25
26 static void operator delete(void*, void*) throw()
27 { }
28
29 static void* operator new[](size_t size) throw(std::bad_alloc)
30 { return operator new (size); }
31
32 static void* operator new[](size_t size,void* ptr) throw()
33 { return ptr; }
34
35 static void* operator new[](size_t size, const std::nothrow_t&) throw()
36 { return operator new (size, std::nothrow); }
37
38 static void operator delete[](void* ptr) throw()
39 { operator delete (ptr); }
40
41 static void operator delete[](void* ptr, const std::nothrow_t&) throw()
42 { operator delete (ptr); }
43
44 static void operator delete[](void*, void*) throw()
45 { }
46};
47
48template<class Alloc>
49class object_pool_impl
50{
51public:
52 template<typename T>
53 static T* construct()
54 {
55 T* const p = static_cast<T*>(Alloc::malloc(sizeof(T)));
56 try { new (p) T(); }
57 catch(){ Alloc::free(p); throw; }
58 return p;
59 }
60
61 template<typename T>
62 static void destroy(T* const ptr)
63 {
64 ptr->~T();
65 Alloc::free(ptr);
66 }
67};
應用
下面代碼中的mem_pool是一種基於自由列表機制實現的內存池,quick_object從new_delete_policy_base<mem_pool>繼承,用於演示定制new和delete的行為,_THROW_EXCEPTION宏用於屏蔽代碼,測試當對象空間分配成功但構造函數拋出異常時,對應的operator delete是否得到調用,而保證釋放內存空間,normal_object是空類,它不從new_delete_policy_base<mem_pool>繼承,用於演示對象池構造和銷毀對象的行為。
1class quick_object : public new_delete_policy_base<mem_pool>
2{
3public:
4 quick_object()
5 {
6#ifdef _THROW_EXCEPTION
7 throw 0;
8#endif
9 cout << "quick_object()" << endl;
10 }
11 ~quick_object()
12 {
13 cout << "~quick_object()" << endl;
14 }
15};
16
17class normal_object
18{
19public:
20 normal_object()
21 {
22 cout << "normal_object()" << endl;
23 }
24 ~normal_object()
25 {
26 cout << "~normal_object()" << endl;
27 }
28};
29
30/**
31 * the following code,if quick_object's construct function throw exception,then result in
32 * c/c++ Run-time system call operator delete correspond to operator new automaticlly.
33 */
34static void unit_test_new_delete_policy()
35{
36 quick_object* obj = NULL;
37
38 try {
39 obj = new quick_object; //call simple new
40 delete obj; //call simple delete
41 }catch(){
42 //call simple delete
43 }
44
45 try {
46 obj = new (std::nothrow) quick_object; //call nothrow new
47 delete obj; //call simple delete
48 }catch(){
49 // call nothrow delete
50 }
51
52 try {
53 char ptr[sizeof(quick_object)];
54 obj = new (ptr) quick_object; //call placement new
55 }catch(){
56 //call placement delete
57 }
58
59 try{
60 obj = new quick_object[10]; //call simple new[]
61 delete []obj; //call simple delete[]
62 }catch(){
63 //call simple delete[]
64 }
65
66 try {
67 obj = new (std::nothrow) quick_object[10]; //call nothrow new[]
68 delete []obj; //call simple delete[]
69 }catch(){
70 //call nothrow delete[]
71 }
72
73 try {
74 char ptr[sizeof(quick_object[10])];
75 obj = new (ptr) quick_object[10]; //call placement new[]
76 }catch () {
77 //call placement delete[]
78 }
79}
80
81/**
82 * class quick_object is inherited from class new_delete_policy_base<mem_pool> that has implement
83 * operator new and delete,so that call placement new in template member construct of class obj_pool.
84 */
85static void unit_test_obj_pool()
86{
87 typedef object_pool_impl<mem_pool> obj_pool;
88
89 try{
90 quick_object* obj = obj_pool::construct<quick_object>();
91 obj_pool::destroy(obj);
92 }catch () {
93
94 }
95 //class normal_object's construct function do not throw exception.
96 normal_object* obj = obj_pool::construct<normal_object>();
97 obj_pool::destroy(obj);
98}