智能指针是一种资源管理类,通过对原始指针进行封装,在资源管理对象进行析构时对指针指向的内存进行释放;通常使用引用计数方式进行管理。
一个基本实现如下:
1 class Counter{ 2 public: 3 friend Class SmartPointer; 4 Counter() { 5 ptr = NULL; 6 cnt = 0; 7 } 8 9 Counter(Object* p) { 10 ptr = p; 11 cnt = 1; 12 } 13 14 ~Counter() { 15 delete ptr; 16 } 17 private: 18 Object* ptr; 19 int cnt; 20 }; 21 22 Class SmartPointer { 23 public: 24 SmartPointer(Object* p) { 25 ptr_counter = new Counter(p); 26 } 27 28 SmartPointer(const SmartPointer& sp) { 29 ptr_counter = sp.ptr_counter; 30 ptr_counter->cnt++; 31 } 32 33 ~SmartPointer() { 34 ptr_counter->cnt--; 35 if (ptr_counter->cnt == 0) { 36 delete ptr_counter; 37 } 38 } 39 40 SmartPointer& operator=(const SmartPointer& sp) { //别忘了const 41 ptr_counter->cnt--; 42 if (ptr_counter->cnt == 0) { 43 delete ptr_counter; 44 } 45 ptr_counter = sp.ptr_counter; 46 ptr_counter->cnt++;
return *this; // 赋值运算符重载都要返回*this 47 } 48 private: 49 Counter* ptr_counter; 50 };
Counter里的指针是Object*,因为是它也是指向这块内存的。
引用计数类Counter相当于对指针和cnt变量的一个包装。智能指针里把所有Object*类型的指针都转换成Counter*类型来处理。
follow up:
一、如何获取智能指针所包装的指针和对象?
solution1:(naive)
定义Object* GetPtr(), Object& GetObject(), 直接返回指针和指针的解引用。
solution2:(good)
对指针的 ->(成员访问)和 *(解引用)进行运算符重载。【运算符重载原理见此文】
即,在成员函数定义里加入这两个:
1 Object* operator->() { 2 return ptr_counter->ptr; 3 } 4 5 Object& operator*() { 6 return *(ptr_counter->ptr); 7 }
注意:成员访问符号->的重载是有递归性质的。
二、引用计数带来的问题:循环引用
如何解决循环引用?
把循环引用的一方使用弱引用(weak_ptr),即可解除循环引用。 ref ref2
三、auto_ptr, unique_ptr, shared_ptr的比较
http://blog.csdn.net/worldwindjp/article/details/18843087
目前最基本的垃圾收集算法有四种:
1. 标记-清除算法(mark-sweep),
2. 标记-压缩算法(mark-compact),
3. 复制算法(copying)
4. 引用计数算法(reference counting)
具体原理:(示意图可参考ref0)
1.标记-清除算法 ref
缺点:造成大量内存碎片
2.标记-压缩算法
该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
3.复制算法
它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
缺点:却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。如果存活对象很多,那么Copying算法的效率将会大大降低。
4.引用计数算法
缺点:循环引用问题
Generational Collection(分代收集)算法
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少;
而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。