原文:http://www.lxzhu.com/blog/archives/251
在Osg中有一个类被使用的最多,那就是ref_ptr,OSG中提供了一种自动管理内存的机制,这种机制涉及到两个类:第一个就是ref_ptr,另外一个就是osg::Referenced.
osg::Reference支持通过引用计数的方式来控制自己的生命周期,它期望,每次被一个新客户引用的时候,新客户能给他添加引用计数;而当客户不需要再使用它的时候,希望客户能够减少引用计数.如果引用计数达到0,那么osg::Reference就会析构自己,释放内存.但是这个过程对客户要求比较高,谁能记得住每次都添加引用和减少引用呢? ref_ptr能!这也是它为什么存在的原因.ref_ptr重载了大量的操作符来实现这个添加引用和减少引用的过程.
下面是一个使用ref_ptr的基本原则:
1.如果希望用ref_ptr来管理内存,你的类需要继承osg::Referenced
2.如果你的类的对象想要持有一个osg::Referenced的派生类的指针,你应该持有一个ref_ptr然后,让ref_ptr来持有一个指针
3.如果你只是在一个函数中短时间的使用一个osg::Referenced的派生类的指针,你不需要使用ref_ptr,直接用就可以了.如果你在短时间使用后,会传递给其他的对象来持有这个指针,你也不需要用ref_ptr,因为通过ref_ptr来长时间持有这个对象是其他的对象的责任了.
4.如果有两个类都从osg::Referenced派生,而你想继承这两个类,那就有麻烦了。Osg中的派生都没有用virtual修饰符,所以你的类对象中会出现两份osg::Referenced的东西.ref_ptr实际上不知道该怎么处理了.如果是一个严格的编译器,编译器甚至会报错!.这个时候,你必须做出选择,从其中一个派生,而另一个则采用组合的方式来处理.多继承就是这一点不好.不过话说回来,在代码中应该尽量不使用多继承,除非是从完全没有成员的纯抽象类继承(纯抽象类当做接口用).
下面来一段ref_ptr的代码看看:
2 {
3 if (_ptr == ptr) return * this ;
4 T * tmp_ptr = _ptr;
5 _ptr = ptr;
6 if (_ptr)
7 _ptr -> ref ();
8 // unref second to prevent any deletion of any object which might
9 // be referenced by the other object. i.e rp is child of the
10 // original _ptr.
11 if (tmp_ptr)
12 tmp_ptr -> unref();
13 return * this ;
14 }
在给ref_ptr赋值的时候,它首先把新对象给ref一下,然后把原来的对象给unref一下.这个持续比较重要,如果反过来写就不行了.你想想看,如果我把同一个指针给它两次,如果反过来写,对象就会被销毁了! 然后是Referenced的代码:
2 {
3 #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
4 ++ _refCount;
5 #else
6 if (_refMutex)
7 {
8 OpenThreads::ScopedLock < OpenThreads::Mutex > lock ( * _refMutex);
9 ++ _refCount;
10 }
11 else
12 {
13 ++ _refCount;
14 }
15 #endif
16 }
17
18 inline void Referenced::unref() const
19 {
20 #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
21 bool needDelete = ( -- _refCount == 0 );
22 #else
23 bool needDelete = false ;
24 if (_refMutex)
25 {
26 OpenThreads::ScopedLock < OpenThreads::Mutex > lock ( * _refMutex);
27 -- _refCount;
28 needDelete = _refCount <= 0 ;
29 }
30 else
31 {
32 -- _refCount;
33 needDelete = _refCount <= 0 ;
34 }
35 #endif
36 if (needDelete)
37 {
38 if (getDeleteHandler())
39 deleteUsingDeleteHandler();
40 else
41 delete this ;
42 }
43 }
去掉多线程控制的代码,其原理是很好理解的。