OSG中的智能指针
智能指针对于C++程序员来说并不是一个陌生的东西,它在c++的内存管理中有着很广泛的应用(在最开始的时候,由于对智能指针不了解,还天真的以为智能指针就是osg的独创,想起来还正是有点太傻了,当然这也和自己的C++基础有关,毕竟那会儿也才刚刚接触c++)。
说到内存管理,简单来说就是内存的创建和回收,但是这时候就会出现一个问题,当我们要使用一个东西的时候,我们肯定会想着去创建它,但是当我们使用完之后还能想的起来要去删除它所占用个的内存空间吗?这个大家可能就不能保证了。那怎么办呢?那就只能等到程序结束之后,系统再释放掉这段内存空间了。这会儿肯定有人想说,我这儿不就new了一小段内存空间吗,内存空间那么大,就这么一点也不算啥。额,这个要是都这么想的话,那就完蛋了,积少成多、滴水穿石各个地方浪费的内存空间积攒到一起这也是一个很可观的数字啊。而且有的程序可能跑一会儿还行,但是很多情况下,一个程序可能会运行一天、一个月、甚至更久,这么看来,内存管理还是挺重要的。
既然说要对内存进行管理,那么如何来管理呢。大家可能经常会听到这样一句话——“用栈中的空间来管理堆中的内存。”这句话是什么意思呢?在解释之前,我们需要先来看看C++中的内存分区,与我们一般觉得内存就是一整块不同,C++中内存是分成了不同的区域的,一般来说有以下几个区域:
静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。
栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。
大家可以看到,栈区是会在函数结束之后自动释放掉的,而堆区则不会,所以聪明的程序猿前辈们想出了一个办法——用栈中的内存管理堆中的内存,具体到OSG中,那就是通过模板类ref_ptr(以继承自Reference类子类作为模板参数的模板类)以及Reference类(在OSG中基本上所有涉及到内存管理的类都是继承Reference类的)的配套使用来实现对内存的管理。在Reference类中他维护了一个_refCount(引用计数)变量来记录当前对象被引用的次数,并且提供了ref()与unref()两个方法来实现对引用计数变量_refCount的++和--操作,而在模板类ref_ptr中则主要通过调用这两个方法来实现对引用计数变量的管理,进而实现对内存的管理。在ref_ptr类中维护了一个_ptr变量用于保存其需要进行管理的指针。例如:
osg::ref_ptr<osg::Group> root = new osg::Group;
上面这一段代码中,定义了一个root变量,这个变量是位于栈上面的,它会在函数结束之后自动释放掉,而我们在后面使用new操作符创建的一个osg::Group对象则是分配在堆上面的,是不会自动释放的,除非等到整个程序结束。将他们通过“=”符号连接起来,这样就能用智能指针来管理我们创建的这个osg::Group对象了。具体如何管理,我们来看看这两个类的源代码就了然了。
#include <osg/Config>
namespace osg {
template<typename T> class observer_ptr;
/** Smart pointer for handling referenced counted objects.*/
template<class T>
class ref_ptr
{
public:
typedef T element_type;
//一系列的构造函数,第一个为默认构造函数,
ref_ptr() : _ptr(0) {}
//第二个需要一个类型为“T*”类型的参数,即需要一个继承自Reference类的对象指针作为参数,并且通过一个if语句判断是否赋值成功,如果成功则调用其所管理的对象指针的ref方法使引用计数加