In fact, Ptr alone can accomplish the task mentioned below.
Implementation see Ptr.h, main2.cpp. In C++11, we also have a better choice: std::shared_ptr (as you can see in main3.cpp).
main2.cpp
1 #include "Ptr.h" 2 3 #include <iostream> 4 5 using namespace std; 6 7 class Box 8 { 9 public: 10 void dosomething() { cout << "Box dosomething" << endl; } 11 Box() { cout << "Box cons" << endl; } 12 ~Box() { cout << "Box des" << endl; } 13 }; 14 15 Ptr<Box> global; 16 17 18 Ptr<Box> func() { 19 Ptr<Box> pb(new Box()); 20 return pb; 21 } 22 23 void call(Ptr<Box> ptr) 24 { 25 if(ptr) 26 ptr->dosomething(); 27 else 28 cout << "ptr is null" << endl; 29 } 30 31 32 33 int main() 34 { 35 Ptr<Box> p = func(); 36 p->dosomething(); 37 (*p).dosomething(); 38 Ptr<Box> p2 = p; 39 call(p2); 40 p2.reset(); 41 cout << "after p2.reset" << endl; 42 global = p; 43 p.reset(); 44 call(p); 45 (*global).dosomething(); 46 global.reset(); 47 cout << "after global.reset" << endl; 48 return 0; 49 }
Ptr.h
1 #ifndef PTR_H 2 #define PTR_H 3 4 #include <cstddef> 5 6 template <typename TYPE> 7 class Ptr { 8 9 public: 10 void reset() 11 { 12 dec_use(); 13 } 14 Ptr& operator=(const Ptr<TYPE> ©) 15 { 16 dec_use(); 17 obj = copy.obj; 18 use_count = copy.use_count; 19 if (use_count) ++*use_count; 20 return *this; 21 } 22 TYPE* operator->() { return obj; } 23 TYPE& operator*() { return *obj; } 24 const TYPE* operator->() const { return obj; } 25 const TYPE& operator*() const { return *obj; } 26 operator bool() const { return (obj != NULL); } 27 28 Ptr(): obj(NULL), use_count(NULL) {} 29 Ptr(TYPE *obj_): obj(obj_), use_count(new int(1)) {} 30 Ptr(const Ptr ©): obj(NULL), use_count(NULL) 31 { 32 obj = copy.obj; 33 use_count = copy.use_count; 34 if (use_count) ++*use_count; 35 } 36 ~Ptr() 37 { 38 dec_use(); 39 } 40 private: 41 void dec_use() // decrease use_count 42 { 43 if (use_count != NULL) { 44 if( --*use_count == 0) { 45 delete obj; 46 delete use_count; 47 } 48 obj = NULL; 49 use_count = NULL; 50 } 51 } 52 TYPE *obj; // the actual object 53 int *use_count; // number of Ptr objects point to 'obj' 54 }; 55 56 #endif // PTR_H
main3.cpp
1 #include <memory> 2 3 #include <iostream> 4 5 using namespace std; 6 7 class Box 8 { 9 public: 10 void dosomething() { cout << "Box dosomething" << endl; } 11 Box() { cout << "Box cons" << endl; } 12 ~Box() { cout << "Box des" << endl; } 13 }; 14 15 shared_ptr<Box> global; 16 17 18 shared_ptr<Box> func() { 19 shared_ptr<Box> pb(new Box()); 20 return pb; 21 } 22 23 void call(shared_ptr<Box> ptr) 24 { 25 if(ptr) 26 ptr->dosomething(); 27 else 28 cout << "ptr is null" << endl; 29 } 30 31 32 33 int main() 34 { 35 shared_ptr<Box> p = func(); 36 p->dosomething(); 37 (*p).dosomething(); 38 shared_ptr<Box> p2 = p; 39 call(p2); 40 p2.reset(); 41 cout << "after p2.reset" << endl; 42 global = p; 43 p.reset(); 44 call(p); 45 (*global).dosomething(); 46 global.reset(); 47 cout << "after global.reset" << endl; 48 return 0; 49 }
---------------------------------------- stupidest iead I've ever seen -----------------------------------------
| |
| |
The idea is to create a Ptr type that acts like a reference in Java.
And A Garbage Collector (MemMgr) type that acts like a garbage collector in Java.
Just a toy. :D
Question: why not delete all memory fragments managed by MemMgr in its destructor?
Answer: If you want to delete a piece of memory, you must cast the void* pointer to the exact type of that memory. However, there's no way for a MemMgr to know the type of the memory pieces, because type information is not managed by MemMgr. And you can't use the free function from <cstdlib>. For example, if you write "MemMgr *p = new MemMgr; free(p);" you'll find that the destructor of MemMgr is not called. And As shown in "test.cpp". "free" only works in pair with "malloc" or "realloc" etc functions in <cstdlib>. "delete" should work in pair with "new".
see this question: http://stackoverflow.com/questions/1518711/how-does-free-know-how-much-to-free
test.cpp
1 #include "MemMgr.h" 2 #include <cstdlib> 3 4 int main() 5 { 6 MemMgr *p = new MemMgr; 7 free(p); 8 return 0; 9 }
main.cpp
1 #include "MemMgr.h" 2 3 #include <iostream> 4 5 using namespace std; 6 7 class Box 8 { 9 public: 10 void dosomething() { cout << "Box dosomething" << endl; } 11 Box() { cout << "Box cons" << endl; } 12 ~Box() { cout << "Box des" << endl; } 13 }; 14 15 Ptr<Box> global; 16 MemMgr mgr; 17 18 19 Ptr<Box> func() { 20 Ptr<Box> pb = mgr.regist(new Box()); 21 return pb; 22 } 23 24 25 26 int main() 27 { 28 Ptr<Box> p = func(); 29 p->dosomething(); 30 (*p).dosomething(); 31 Ptr<Box> p2 = p; 32 p2->dosomething(); 33 cout << "end of main" << endl; 34 global = p2; 35 return 0; 36 }
MemMgr.h
1 #ifndef MEMMGR_H 2 #define MEMMGR_H 3 4 #include <map> 5 6 template <typename TYPE> 7 class Ptr; 8 9 /** 10 MemMgr take the idea of Garbage Collector 11 from the Java language. It's just much simple 12 and limited. 13 */ 14 class MemMgr 15 { 16 template <typename T> friend class Ptr; 17 private: 18 typedef unsigned long count; 19 template <typename T> void login(T* ptr_obj); 20 template <typename T> void logout(T* ptr_obj); 21 std::map<void*, count> cmap; 22 public: 23 MemMgr(); 24 /** 25 Client is responsible to ensure obj is in the heap, 26 and make sure only use Ptr objects rather than ordinary 27 pointers when manipulating objects managed by MemMgr. 28 29 Otherwise the behavior of the MemMgr is undefined. 30 31 If MemMgr is destroyed before any Ptr object managed 32 by it, all Ptr objects managed by that MemMgr are corrupted 33 and their behavior is undefined, which eventually leads to 34 memory leak. 35 36 So it's crucial to make sure MemMgr is not destroyed 37 before ALL Ptr objects managed by it are destroyed. 38 */ 39 template <typename T> Ptr<T> regist(T *obj); 40 ~MemMgr(); 41 42 }; 43 44 /** 45 Ptr acts like a reference in java. 46 */ 47 template <typename TYPE> 48 class Ptr { 49 50 friend class MemMgr; 51 52 public: 53 Ptr& operator=(const Ptr<TYPE> ©) 54 { 55 if(copy) { 56 logout(); 57 obj = copy.obj; 58 mgr = copy.mgr; 59 copy.mgr->login(&obj); 60 } // else leaves obj and mgr NULL 61 return *this; 62 } 63 TYPE* operator->() { return obj; } 64 TYPE& operator*() { return *obj; } 65 const TYPE* operator->() const { return obj; } 66 const TYPE& operator*() const { return *obj; } 67 operator bool() const { return ( (obj != NULL) && (mgr != NULL) ); } 68 69 Ptr(): obj(NULL), mgr(NULL) {} 70 Ptr(const Ptr ©): obj(NULL), mgr(NULL) 71 { 72 if(copy) { 73 obj = copy.obj; 74 mgr = copy.mgr; 75 copy.mgr->login(obj); 76 } 77 } 78 ~Ptr() 79 { 80 logout(); 81 } 82 private: 83 Ptr(TYPE *_obj, MemMgr *_mgr): obj(_obj), mgr(_mgr) 84 { 85 mgr->login(obj); 86 } 87 void logout() { 88 if (*this) { 89 mgr->logout(obj); obj = NULL; mgr = NULL; 90 } 91 } 92 TYPE *obj; 93 MemMgr *mgr; 94 }; 95 96 97 template <typename T> Ptr<T> MemMgr::regist(T *obj) 98 { 99 return Ptr<T>(obj, this); 100 } 101 102 template <typename T> 103 void MemMgr::login(T* ptr_obj) 104 { 105 std::map<void*, count>::iterator iter = cmap.find(ptr_obj); 106 if (iter != cmap.end()) { 107 ++(iter->second); 108 } else { 109 cmap.insert(std::pair<void*, count>(ptr_obj, 1)); 110 } 111 } 112 113 template <typename T> 114 void MemMgr::logout(T* ptr_obj) 115 { 116 std::map<void*, count>::iterator iter = cmap.find(ptr_obj); 117 if (iter != cmap.end()) { 118 --(iter->second); 119 if (iter->second == 0) { 120 T *p = (T*)(iter->first); 121 delete p; 122 } 123 } 124 } 125 126 #endif // MEMMGR_H
MemMgr.cpp
1 #include "MemMgr.h" 2 3 #include <iostream> 4 5 using namespace std; 6 7 MemMgr::MemMgr() 8 { 9 cout << "MemMgr cons" << endl; 10 } 11 12 MemMgr::~MemMgr() 13 { 14 cout << "MemMgr des" << endl; 15 }