查看: 4215|回复: 7

关于osg::ref_ptr<>的一些问题

[复制链接]

该用户从未签到

发表于 2012-3-21 17:09:40 | 显示全部楼层 |阅读模式
关于智能指针一直存在很多疑惑,有些东西我认为应该是那样子,但是有不确定。所以来此请教一下。

关于osg::ref_ptr里面的get()与release()函数。
通过查看代码发现,get()函数是纯粹的返回_ptr指针,而relesae()则要麻烦一点。
T* release() {
        T* tmp=_ptr;
        if (_ptr)
             _ptr->unref_nodelete();
        _ptr=0;
        return tmp;
}
可以看出这里面对_ptr进行了一次引用计数减的操作并且不做删除操作。顺便查看了一下unref_nodelete()和unref()这两个函数,前者内部只是把内部的引用计数进行了减1操作,后者多了一些判断是否删除指针的操作。当然还有一些判断宏定义的,我把非活动预处理器块忽略了。

然后转到另一个地方。
osg::ref_ptr<osg::Node> node = new osg::Node;
node.get();
node.release();
对于上面的代码,在执行了node.get()后,node没有其他的改变,只是将内部的指针提取出来。node还是可以继续使用的。但是node.release()执行完后,node这个变量不能继续使用。但是堆上的内存仍在,node的数据仍然可以继续使用。
以上的理解不知道是否正确?

下面轮到真正困扰我的……问题总是一环扣一环,一个解决不掉,整个链条都会杯具。

问题就是函数返回值。我举出一些例子,并提出我的看法。我不知道这里面有哪些是正确的,哪些是我异想天开写出来的,见笑。
示例1:
osg::ref_ptr<osg::Node> createNode1(){
        osg::ref_ptr<osg::Node> node = new osg::Node;
        blablabla...
        return node;
}
int main(){
        blablabla...
        osg::ref_ptr<osg::Node> node = new osg::Node;
        node = createNode1();
        blablabla...
}
上面的用法是我从网络上查到的。里面说引用的是
Don Burns《Using Reference Pointers in Producer and OpenSceneGraph》
这里面提出了5个规则:
        规则1对所有从Referenced 继承的类,都用ref_ptr<>
        规则2绝对不要返回ref_ptr<> 指向的地址,返回ref_ptr<> 它自己
        规则3绝对不要用ref()、unref(),(或者release()、unref_nodelete())除非你真的真的真的知道你在干什么
        规则4任何Referenced 的派生类(无论直接或间接),析构函数都要设为protected,使对象不会被分配到栈上
        规则5(规则1的例外):循环引用时,当我们知道此对象在当前范围内会被另外的ref_ptr 引用,就有必要(小心地)使用简单指针
上面的例子就是根据这些规则写的。不知道这样使用是否正确?在main函数里进行赋值的时候,是否需要对node进行new空间呢?赋值会对左操作数引用计数-1,这岂不是刚开辟的空间又没了么……这个地方我有点混乱……


示例2:
osg::ref_ptr<osg::Node> createNo
de2(){
        osg::ref_ptr<osg::Node> node = new osg::Node;
        blablabla...
        return node.get();
}
int main(){
        blablabla...
        osg::ref_ptr<osg::Node> node = new osg::Node;   

        node = createNode2();
        blablabla...
}
这个用法是我从OSG编程指南上看到的。我最初也基本上都是这么写的。和示例1的区别就在于createNode2函数最后返回的是node.get(),这个地方返回值发生了默认转换了吧?还有,在main里面有必要new空间么?

示例3:
osg::Node* createNode3(){
        osg::ref_ptr<osg::Node> node = new osg::Node;
        blablabla...
        return node.release();
}
int main(){
        blablabla...
        osg::Node* = createNode3();
        blablabla...
        root->addChild(createNode3());
        blablabla...
}
这是我在OSG CookBook示例代码中看到的用法。这里用到了release()函数。我把这想象成返回的是一个纯粹的指针,不再含有“智能”的属性。不知道是否是这样子的?还有,如果在main函数内,仍然想用智能指针的话,该怎么做呢?在main函数下用的是C++指针,是否失去了智能指针的效果了呢?

以上三个示例,第一个是我编的,后面两个是在现成的代码中找的用法。对于智能指针的理解,我仍然有一些混乱的地方。比如,上面三个例子中的main函数下,node变量new空间的必要性。函数返回值写哪一种比较好?是返回指针,还是直接返回智能指针?

我也尝试这自己解决这些问题,但是对于调试追踪什么的……实在是不行……无从下手……再加上我查看了一下代码,发现示例2和示例3同时存在于我的代码中……有一段时间确实发生过混乱……而且那段时间我乱用release()……
顺便说一句……写完这篇帖子,我突然想起了Array经常说的那句:在Beginner Guide上有关于这方面的解释……现在去看看那本书上是怎么说的……

同时希望大家能够帮我解惑……真诚的感谢大家……


该用户从未签到

发表于 2012-3-22 15:58:39 | 显示全部楼层
个人认为:
1、
上面三个例子中的main函数下,node变量new空间的必要性
三个例子都没有必要在main函数中去new。因为你最终要的结果是在create函数内分配。
2、
函数返回值写哪一种比较好?是返回指针,还是直接返回智能指针?
两种返回都可以,但请注意这里有效率区别,返回智能指针效率要低一点。返回指针要高一点。但要谨记的一点是如果返回指针且在函数内部使用的ref_ptr引用他,这是必须调用.release(),否则当你使用返回值时返回的指针已经是野指针,其内容已被释放再使用时导致崩溃。
个人认为采用release的方式返回效率高,也不存在问题,比较推荐!

该用户从未签到

发表于 2012-3-22 16:02:55 | 显示全部楼层
然后转到另一个地方。
osg::ref_ptr<osg::Node> node = new osg::Node;
node.get();
node.release();
对于上面的代码,在执行了node.get()后,node没有其他的改变,只是将内部的指针提取出来。node还是可以继续使用的。但是node.release()执行完后,node这个变量不能继续使用。但是堆上的内存仍在,node的数据仍然可以继续使用。
以上的理解不知道是否正确?

node也可以用,只是内部指针已经清空,不可以node->方式使用,但node可以接受其他指针后继续使用。

该用户从未签到

 楼主| 发表于 2012-3-22 17:20:18 | 显示全部楼层
本帖最后由 daiday 于 2012-3-22 17:20 编辑
但要谨记的一点是如果返回指针且在函数内部使用的ref_ptr引用他,这是必须调用.release(),否则当你使用返回值时返回的指针已经是野指针,其内容已被释放再使用时导致崩溃。

osg::Node* create()
{
        osg::ref_ptr<osg::Node> node = new osg::Node;
        return node.release(); //return node.get();
}
经过反复调试…我终于搞清楚了……如果上面的情况下,使用release函数,会将地址返回去,对于node节点来说,它的refCount减1,而且内部的_ref会赋值为0,这样在析构的时候,会跳过unref()这个函数。但是如果使用return node.get(),返回的地址也没有问题。但是在函数结束的时候,智能指针的析构函数会进行一次unref(),从而使得那个地址内部什么东西也没有。再使用的话,就会导致崩溃。原来如此!Thank you!

该用户从未签到

发表于 2012-3-22 19:54:34 | 显示全部楼层
osg::ref_ptr<osg::Node> create()
{
        osg::ref_ptr<osg::Node> node = new osg::Node;
         return node.get();
}
这种写法也没什么问题吧?我很多程序是这么写的。

该用户从未签到

 楼主| 发表于 2012-3-22 20:55:06 | 显示全部楼层
本帖最后由 daiday 于 2012-3-22 20:55 编辑

osg::ref_ptr<osg::Node> create()
{
        osg::ref_ptr<osg::Node> node = new osg::Node;
         return node.get();
}
这种写法也没什么问题吧?我很多程序是这么写的。


这种写法是正确的。这就是示例2的做法……不过貌似现在都比较推荐示例3的写法。刚才调试了一下,发现如果返回的是智能指针的话,还要多调用一次ref_ptr的构造函数。返回的如果是普通指针,则不需要。

该用户从未签到

发表于 2012-3-22 22:26:03 | 显示全部楼层
osg::ref_ptr<osg::Node> create()
{
        osg::ref_ptr<osg::Node> node = new osg::Node;
         return node.get();
}
这种写法也没什么问题吧?我很多程序是这么写的。

不推荐,任何时候都要尽可能的避免隐式转换!因为隐式转换会产生临时变量导致效率低下

该用户从未签到

发表于 2012-12-14 16:12:47 | 显示全部楼层
很不错,看来代码的理解还需要进一步的深入~学习了~
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表