OSG智能指针---Referenced类
osg学习笔记
1:
osg的x正方形向右,y正方向朝里,z正方向朝外
opengl的x正方向向右,y正方向朝上,z正方向朝外
[原创]OSG Discussion–1:Referenced类与智能指针(转)
2:Referenced类与智能指针(转)
在OpenSceneGraph中,智能指针(Smart pointer)的概念指的是一种类的模板,它针对某一特定类型的对象(即Referenced类及其派生类)构建,提供了自己的管理模式,以避免因为用 户使用new运算符创建对象实例之后,没有及时用delete运算符释放对象,而造成部分内存空间被浪费的后果,也就是所谓的内存泄露错误。
由于OSG中与场景图形有关的大多数类均派生自Referenced类,因此OSG大量使用了智能指针来实现场景图形节点的管理。智能指针的使用为用户提 供了一种自动内存释放的机制,即,场景图形中的每一个节点均关联一个内存计数器,当计数器的计数减到零时,该对象将被自动释放。而用户如果希望释放整个场 景图形的节点的话,则只需要删除根节点,根节点以下的所有分支节点均会因此被自动删除,不用担心内存泄露的问题。
要使用OSG的智能指针,需要满足以下两个条件:
1、用户的类必须派生自Referenced类,这样才能使用与其自身关联的内存计数器;
2、使用智能指针模板osg::ref_ptr<class T>来定义类的实例,当用户使用该模板定义实例时,内存计数器即被启用并加一;同理,当ref_ptr模板超出其生命范围时,类实例的内存计数器将被减一,如果减到零则对象自动被释放。
此外,要使用智能指针,程序中应当引用以下的头文件:
#include <osg/ref_ptr>
一个使用智能指针的例子如下:
void exampleFunc()
{
osg::ref_ptr<osg::Group> root = new osg::Group;
osg::ref_ptr<osg::Geode> node1 = new osg::Geode;
osg::ref_ptr<osg::Geometry> geo1 = new osg::Geometry;
printf("%d, %d, %d\n", root->referenceCount(), node1->referenceCount(), geo1->referenceCount());
root->addChild(node1.get());
node1->addDrawable(geo1.get());
printf("%d, %d, %d\n", root->referenceCount(), node1->referenceCount(), geo1->referenceCount());
}
这个例子本身并没有什么意义,但是可以通过它了解智能指针的运作流程。
在解读这个例子之前,首先了解一下与ref_ptr和Referenced类相关的主要成员和运算符:
Referenced类
void ref()
这个公共函数使得Referenced类实例的内存计数器值加一。
void unref()
这个公共函数使得Referenced类实例的内存计数器值减一,如果计数器值为零,那么它自动尝试将类的实例删除,释放相应的内存。
int referenceCount() 返回当前内存计数器的数值。
ref_ptr
ref_ptr() 构造函数,不过它什么也不做。用例为:osg::ref_ptr<osg::Node> node1;
ref_ptr(T* ptr) 构造函数,并为其对象分配新的内存空间,同时对象的内存计数器值加一。
用例为:osg::ref_ptr<osg::Node> node1 = new osg::Node;
ref_ptr(const ref_ptr& rp) 构造函数,其对象将指向一个已有的智能指针对象,同时对象的内存计数器值加一。
用例为:osg::ref_ptr<osg::Node> node2 = node1;
~ref_ptr() 析构函数,执行时对象的内存计数器值减一。
ref_ptr& operator = (const ref_ptr& rp) 重载的赋值运算符,用例为:node2 = node1;将node2指向node1,同时将node2(也就是node1)的内存计数器值加一。
ref_ptr& operator = (T* ptr) 重载的赋值运算符,用例为:node2 = new osg::Node;将node2指向一个类的实例,同时将node2的内存计数器值加一。
T& operator*() const 返回类实例的值。例如:osg::ref_ptr<osg::Node> node1 = new osg::Node;则*node1表示osg::Node
T* operator->() const 返回类的实例。例如:osg::ref_ptr<osg::Node> node1 = new osg::Node;则node1->…表示(osg::Node*)->…
T* get() const 返回类的实例。例如:osg::ref_ptr<osg::Node> node1 = new osg::Node;则node1.get()表示osg::Node*
bool valid() 返回指针是否有效的标志。
void swap(ref_ptr& rp) 将目前指针所指向的内容与用户输入的数据进行交换。用例为:node2.swap(node1); //交换两个指针的位置
再看刚才的例子程序,它主要完成了这样的功能:
1、 新建两个节点root和node1,以及一个几何图形geo1;
2、 调用智能指针的get方法,将node1作为root的子节点加入(addChild);
3、 调用智能指针的get方法,将geo1作为node1的绘图数据加入(addDrawable)。
此外,程序还调用referenceCount方法,观察内存计数器的数值。
在主函数中调用此子函数,编译并运行,观察显示的结果,应为:
1, 1, 1
1, 2, 2
可见,当节点和几何图形第一次被创建时,它们的内存计数器自动加一;而将node1作为子节点加入以及将geo1作为图形元件加入的操作,则分别使得这两者的内存计数器再次加一。
使用new运算符使得内存计数器加一,是因为在ref_ptr构造函数中执行了ref()方法。此方法自动为当前实例的内存计数器加一。
函数addChild和addDrawable会使得内存计数器加一,是因为程序中将node1或者geo1加入到一个ref_ptr的向量表中。参照源代码可知,用于保存子节点的向量表为NodeList,其定义为:
typedef std::vector< ref_ptr<Node> > NodeList;
而用于保存Geometry几何数据的向量表为DrawableList,其定义为:
typedef std::vector< ref_ptr<Drawable> > DrawableList;
在执行函数addChild和addDrawable时,使用了向量表模板的push_back方法,将带有智能指针的数据压入向量表中,这一步将使得内存计数器自动加一。具体的执行过程可以参见VC目录下的vector头文件,其中有类同以下的语句段:
……
_Ty _Tmp = _Val;
……
对于NodeList,上文中的_Ty即表示ref_ptr<Node>,而_Val则是压入向量表的数据,因此有:
ref_ptr<Node> _Tmp = node1.get();
参考ref_ptr中第二种构造函数的形式可知,此时系统将调用ref()函数,使得内存计数器再次加一,显然,这一操作对node1也会产生影响。
同理,当执行向量表的pop_back或erase函数时,因为调用了ref_ptr的析构函数,也会使得内存计数器自动减一。执行函数removeChild和removeDrawable即可实现这样的效果。
再看一种常见的情况,代码如下:
for (int i = 0; i < 100; i++)
{
osg::Node* node = new osg::Node;
……
}
一般情况下,在循环中使用new运算符开辟新的内存空间,如果没有及时释放的话,将产生内存泄露的问题。对于上述的程序段,在运行时如果打开任务管理器,则可以看到程序所占的内存值不断上涨,如果不加以制止的话,甚至可能造成计算机崩溃。
现在将该程序段中使用new运算符的语句行改写如下:
//osg::Node* node = new osg::Node;
osg::ref_ptr<osg::Node> node = new osg::Node;
再次运行该程序,可以发现内存增长的现象消失了,智能指针在这里发挥了不可忽视的作用。分析这一段程序的流程,可见:
1、 进入循环后,首先为node分配一块新的内存区域,同时内存计数器自动加一;
2、 执行其余的代码,如果不对node使用addChild等操作,那么计数器的值始终为1;
3、 到达for循环的结束位置,此时临时变量的生命周期已经结束,则执行~ref_ptr(),在其中自动执行unref()对计数器的值减一,则计数器的值为0,系统将自动释放内存区域。
4、新的循环开始,此时原有的内存区域已被释放,没有出现内存泄露的情况。
综上所述,使用智能指针ref_ptr来包装用户的节点类,几何体类等数据,可以有效地进行内存管理,很大程度上避免了内存泄露现象的发生。而在智能指针的使用过程中,还应当注意以下几点:
1、智能指针模板的应用对象必须派生自Referenced类,否则模板将无法使用。例如:
osg::ref_ptr<osg::Vec3> v;
这样的声明是无法编译通过的,因为Vec3类并不是派生自Referenced类,因此也不具有ref()和unref()这样的成员函数,无法与计数器相关联。
2、不可以直接使用delete运算符删除应用智能指针的对象。事实上这样的语句也无法编译通过,阅读Referenced类的源代码可以发 现,Referenced类的析构函数~Referenced()为保护函数(protected类型),直接使用delete运算符调用它是不允许的。
3、不要随意使用ref()和unref()函数来改变内存计数器的值。由于这两个函数都是公共函数,因此这样的操作不会在编译中报错,但是如果内存计数 器的数值在程序运行时减为零,以致其对象被释放,那么下面所有针对此对象的操作均可能导致程序崩溃。而这样的变故在正常使用的情况下是决不会出现的,因 此,除非用户有特殊需要,否则尽量不要直接使用ref()和unref()来改变内存计数器的值。
4、在OSG中,不使用智能指针而是用形如osg::Node * node的声明方式也是可以的。但是在大型程序中,应当尽量统一使用智能指针来进行内存的管理。此外,有的时候没有统一使用ref_ptr的话,程序也可能出现问题。比如这个例子:
osg::Group* exampleFunc()
{
osg::ref_ptr<osg::Group> root = new osg::Group;
osg::ref_ptr<osg::Geode> node1 = new osg::Geode;
root->addChild(node1.get());
……
return root->get();
}
int main(int argc, char** argv)
{
……
osg::Node* a = exampleFunc()->getChild(0);
……
}
由于ref_ptr的生命周期在函数的末尾即告结束,导致函数返回时返回的Group指针其实已经被释放掉了,这样程序编译和链接都不会与错误,但运行时会出现错误,而且这种错误往往难以检测到。为了解决问题,将程序统一修改为ref_ptr的命名方式如下:
osg::ref_ptr<osg::Group> exampleFunc()
{
osg::ref_ptr<osg::Group> root = new osg::Group;
osg::ref_ptr<osg::Geode> node1 = new osg::Geode;
root->addChild(node1.get());
……
return root;
}
int main(int argc, char** argv)
{
……
ref_ptr<osg::Node> a = exampleFunc()->getChild(0);
……
}
就可以运行通过了。函数返回时,ref_ptr将再次把内存计数器的数值加一,保证返回的数据有效,且主函数中仍然可以交由智能指针进行内存管理。
OSG智能指针---Referenced类相关推荐
- C++智能指针管理类
1.程序猿明白的进行内存释放 对于c++程序猿,最头脑的莫过于对动态分配的内存进行管理了.c++在堆上分配的内存.须要程序猿负责对分配的内存进行释放.但有时内存的释放看起来并不件非常轻松的事,例如以下 ...
- C++智能指针模板类
对于常规类指针,可能由于忘记释放内存而导致内存泄漏,有三种智能指针可以解决这类问题. 对于常规指针,它没有析构函数,加入指针成为了对象,那么,在对象过期时就会自动调用析构函数,让析构函数释放指针指向的 ...
- OSG智能指针:osg::ref_ptr
在osg开发时(特别是刚上手),有一点比较重要然后容易忽略的是,在osg程序开发中,要尽量使用osg::ref_ptr<T*>. 这个智能指针构建起osg的对象树(绝大部分osg类都是继承 ...
- 《C++ Primer Plus》16.2 智能指针模板类
智能指针是行为类似于指针的类对象,单这种对象还有其他功能.本节介绍三个可帮助管理动态内存分配的智能指针类.先来看看需要哪些功能以及这些功能是如何实现的.请看下面的函数: void remodel(st ...
- VTK源码阅读--VTK里的智能指针vtkSmartPointer类
vtkSmartPointer类 vtkSmartPointer是一个类模板,为vtkSmartPointerBase超类持有的对象提供自动强制转换. #ifndef vtkSmartPointer_ ...
- 一个自己实现的简单的智能指针模板类
一个智能指针的小程序,使用模板函数开发,附带测试程序 代码如下: ///auto ptr template<typename T> class AutoPtr { struct t_ptr ...
- c++string 加引号_C++|引用计数与shared_ptr智能指针(以实现String类为例)
C++ 中,动态内存的管理是通过一对运算符来完成的,new 用于申请内存空间,调用对象构造函数初始化对象并返回指向该对象的指针.delete接收一个动态对象的指针,调用对象的析构函数销毁对象,释放与之 ...
- 【转】C++面试题(四)——智能指针的原理和实现
C++面试题(四)--智能指针的原理和实现 tanglu2004 http://blog.csdn.net/worldwindjp/ C++面试题(一).(二)和(三)都搞定的话,恭喜你来到这里, ...
- c++ 智能指针_详解 C++ 11 中的智能指针
C/C++ 语言最为人所诟病的特性之一就是存在内存泄露问题,因此后来的大多数语言都提供了内置内存分配与释放功能,有的甚至干脆对语言的使用者屏蔽了内存指针这一概念.这里不置贬褒,手动分配内存与手动释放内 ...
- C/C++学习之路: 智能指针
C/C++学习之路: 智能指针 目录 前言 shared_ptr 1. 前言 在C++中,动态内存的管理是通过一对运算符完成的: new:在动态内存中为对象分配空间并返回一个指向该对象的指针,可以选择 ...
最新文章
- 2018-3-10论文(网络评论非结构化信息表示与应用研究)笔记-----基于证据理论的综合评价模型建立
- JS 中的 Map,Set 和 iterable
- 030_SpringBoot全局属性配置文件
- php背景,php图片背景填充实例
- 蓝桥杯 基础练习 芯片测试
- 创建log文件的代码
- Faiss从入门到实战精通
- 如何用python计算行列式_Python入门教程: 计算范德蒙矩阵的行列式
- 和机器人问问题的软件_如何开发一个特定领域的自动问答机器人(Chat Bot)?
- appscan如何扫描移动应用APP
- 虚幻4英雄联盟模型分享——荆棘之刺婕拉
- deb文件如何安装(安装文件管理器)
- 【Junit Experiment】Junit 软件质量测试实验--日期格式规范性+字符串格式规范性
- win10网络共享需要凭据的解决办法
- Hive SQL 使用不了union all : SemanticException Cartesian products are disabled for safety reasons
- 北邮计算机周安福,周安福
- python爬取微博评论点赞数_Python selenium爬取微博数据代码实例
- 设计商品分类表 mysql_商品分类表设计
- 求数组中的最大差值或最小差值
- 宁波大学计算机王老师,赵杰煜_宁波大学研究生导师信息
热门文章
- 太上老君的炼丹炉之分布式 Quorum NWR
- 小米路由器的服务器无响应怎么回事啊,小米路由器常见故障处理方法
- 花木兰替父从军java_花木兰替父从军 木兰诗原文
- 19、21、22、24英寸液晶屏幕实际尺寸4:3、16:9、16:10详表
- 机器人阿郎_孕晚期胎教故事文字版:机械人阿郎
- python人物关系抽取_Python学习笔记(2) Python提取《釜山行》人物关系
- DCM: 诊断通信管理 (Diagnostic Communiction Manager)
- 在VMware上安装Android虚拟机
- app开发都有哪些基本的开发语言选择?
- 下厨房怎么显示服务器错误,4s只有一个下厨房app显示网络连接失败