osge阶段学习总结3(给自己看的记录)——OSG 智能指针 详解

注:osg版本号为3.6.3

1、Referenced类

osg::Referenced类是几乎所有osg类的父类。
这个类与智能指针修改部分函数如下:(不相关函数略)

class OSG_EXPORT Referenced
{

public:
        //ref()方法,在这个方法中只做了一件事,就是对_refCount进行++运算;
        inline int ref() const;

        /*unref()方法,在此方法中首先对_refCount进行--运算,之后再判断经过—运算之后的
        引用计数是否等于0,若等于0(意味着没有对象引用此段内存空间,可以被释放)
        则将needDelete赋值为true。在函数最后,对needDelete进行判断,如果需要被释放,
        则调用signalObserversAndDelete(true,true)方法,在这个函数中将会具体执行对对象的删除。*/
        inline int unref() const;

        /*对引用计数执行–1 运算。不检查引用计数是否为零,
        不进行内存空间delete或其它方式的内存释放*/       
        int unref_nodelete() const;

        //引用计数值
        inline int referenceCount() const { return _refCount; }
  protected:


        /*在这个函数中具体执行对对象的删除,它首先对其所有观察者进行删除
        (应该是观察者模式之类的,暂时也不是太理解,可以暂时忽略,注意后面才是本节重点)。
        之后再判断是否存在DeleteHandler,当我们需要自定义删除的时候,
        我们可以自己写一个DeleteHandler实现我们想要的功能,
        如果没有,则直接执行delete操作,对对象进行删除  */
        void signalObserversAndDelete(bool signalDelete, bool doDelete) const;

}

Referenced类管理了一个_refCount成员变量,用于记录引用计数。其它函数都是对_refCount成员变量的“++”或“–”管理。所有继承该类的类都有一个引用计数以及相关功能。

2、ref_ptr类。

ref_ptr类是一个类,是一个模板类。
这个类与智能指针修改部分函数如下:(没有不相关函数,但省略一些非活动预处理代码)

template<class T>
class ref_ptr
{
    public:
        typedef T element_type;
        //一系列的构造函数,第一个为默认构造函数,
        ref_ptr() : _ptr(0) {}
        
        /*第二个需要一个类型为“T*”类型的参数,即需要一个继承自Reference类的对象指针作为参数,
        并且通过一个if语句判断是否赋值成功,如果成功则调用其所管理的对象指针的ref方法使引用计数加一。*/
        ref_ptr(T* ptr) : _ptr(ptr) { if (_ptr) _ptr->ref(); }

        //传入一个模板参数相同的ref_ptr对象,如果成功引用,则调用ref是引用计数加一  
        ref_ptr(const ref_ptr& rp) : _ptr(rp._ptr) { if (_ptr) _ptr->ref(); }

        /*复制构造函数,将另一个ref_ptr对象作为参数传入当前对象中
        ——对所管理的指针增加一个引用,适用与模板参数不同的情况 */ 
        template<class Other> ref_ptr(const ref_ptr<Other>& rp) : _ptr(rp._ptr) 
        { if (_ptr) _ptr->ref(); }
        
        //用于接受弱指针作为参数的构造函数。
        ref_ptr(observer_ptr<T>& optr) : _ptr(0) { optr.lock(*this); }
        
        /*析构函数,在此析构函数中,首先调用unref函数使得管理的指针中的引用计数减一,
        之后将当前指针置空。*/  
        ~ref_ptr() { if (_ptr) _ptr->unref();  _ptr = 0; }
//上面是构造函数、拷贝构造函数、复制构造函数和析构函数等///

        /*重载赋值操作符,处理将一个智能指针赋值给另一个智能指针的情况,
        与直接调用ref函数不同,在这个函数中通过调用assign函数来处理。
        例如:osg::ref_ptr<osg::Node> node1 = new osg::Node;
        osg::ref_ptr<osg::Node> node = node1;(ref_ptr都是管理的Node类对象)  */
        ref_ptr& operator = (const ref_ptr& rp)
        {
            assign(rp);
            return *this;
        }

        /*重载赋值操作符,用于处理管理不同类型的智能指针之间的引用。
        例如:osg::ref_ptr<osg::Geode> node1 = new osg::Node;
        osg::ref_ptr<osg::Node> node = node1;(相当于将子类对象转化为父类对象)*/  
        template<class Other> ref_ptr& operator = (const ref_ptr<Other>& rp)
        {
            assign(rp);
            return *this;
        }

        /*重载赋值操作符,用于处理将一个普通指针交由超级指针管理。例如:
        osg::ref_ptr<osg::Node> node1 = new osg::Node;这不就和我们上面的例子中用到的一样么?
        我们new了一个osg::Group,之后将其返回的地址通过这个等于符号传给创建的root对象,
        在我们使用“=”运算符的时候不就是调用的这个函数么?*/
        inline ref_ptr& operator = (T* ptr)
        {
            if (_ptr==ptr) return *this;
            T* tmp_ptr = _ptr;
            _ptr = ptr;
            if (_ptr) _ptr->ref();
            // unref second to prevent any deletion of any object which might
            // be referenced by the other object. i.e rp is child of the
            // original _ptr.
            if (tmp_ptr) tmp_ptr->unref();
            return *this;
        }
//上面是重载的各种赋值函数///

#ifdef OSG_USE_REF_PTR_IMPLICIT_OUTPUT_CONVERSION
        // implicit output conversion
        operator T*() const { return _ptr; }
#else
    .........
#endif

        /*重载与普通指针相关的运算符,如‘*’、‘->’。使其具有指针的功能,
        并设置一个get()方法用于获取智能指针类所管理的指针对象*/ 
        T& operator*() const
        {
#ifdef OSG_USE_REF_PTR_SAFE_DEREFERENCE
            if( !_ptr ) {
                // pointer is invalid, so throw an exception
                throw std::runtime_error(
                    std::string("could not dereference invalid osg pointer ")
                     + std::string(typeid(T).name()));
            }
#endif
            return *_ptr;
        }
        T* operator->() const
        {
#ifdef OSG_USE_REF_PTR_SAFE_DEREFERENCE
            if( !_ptr ) {
                // pointer is invalid, so throw an exception.
                throw std::runtime_error(
                    std::string("could not call invalid osg pointer ") 
                    + std::string(typeid(T).name()));
            }
#endif
            return _ptr;
        }

        T* get() const { return _ptr; }
//上面是重载的方便当作指针调用的函数///


        /*重载运算符‘!’,并设置一个valid()方法用于判断智能指针类所管理的指针对象是有效,
        有效则返回1,无效则返回0; */ 
        bool operator!() const { return _ptr == 0; } // not required
        bool valid() const       { return _ptr!=0; }

        /*release方法,用于在返回一个对象的时,并能防止内存泄露。
        它与get方法最大的区别就是get方法直接返回所管理的指针,
        而此处是先调用unref_nodelete()方法之后才返回其所管理的对象,这在后面会具体讲到。*/  
        T* release() 
        { 
            T* tmp = _ptr; 
            if (_ptr) 
                _ptr->unref_nodelete(); 
            _ptr = 0; 
            return tmp; 
        }

        //交换两个智能指针所管理的对象  
        void swap(ref_ptr& rp) { T* tmp=_ptr; _ptr=rp._ptr; rp._ptr=tmp; }

    private:
        //用于对指针引用时进行一些处理,对新添加的引用执行ref操作,而对以前的引用执行unref操作  
        template<class Other> void assign(const ref_ptr<Other>& rp)
        {
            if (_ptr==rp._ptr) return;

            T* tmp_ptr = _ptr;
            _ptr = rp._ptr;
            if (_ptr) _ptr->ref();
            // unref second to prevent any deletion of any object which might
            // be referenced by the other object. i.e rp is child of the
            // original _ptr.
            if (tmp_ptr) tmp_ptr->unref();
        }

        //将其他模板参数的类设置为当前类的友元。  
        template<class Other> friend class ref_ptr;

        T* _ptr;//私有变量,用于存放所管理的指针  
};

虽然调用的时候把它看成一个指针完全可以。但是,ref_ptr是一个类,模板类。所以如下代码:

osg::ref_ptr<osg::Node> node = osgDB::readRefNodeFile("cow.osg");

其中,node是osg::ref_ptr类的对象,< osg::Node >是给模板类传递的类。类似模板类定义如下:

std::vector<int> intList;

其中,intList是std::vector的对象,< int >是给模板类传递的类。

3、调用实例说明。

调用实例代码如下:

osg::Node* create1()
{
  	  osg::Node* nodeTemp = osgDB::readNodeFile("cow.osg");
     return  nodeTemp ;
}
osg::Node* creat2()
{
   	osg::ref_ptr<osg::Node> nodeTemp = osgDB::readNodeFile("cow.osg");
  	 return nodeTemp.get();
}
osg::Node* creat3()
{
   osg::ref_ptr<osg::Node> nodeTemp = osgDB::readNodeFile("cow.osg");
   return nodeTemp.release();
}
int main()
{
		osg::ref_ptr<osg::Node> node1 = create1();
		osg::ref_ptr<osg::Node> node2 = create2();
		osg::ref_ptr<osg::Node> node3 = create3();
		osg::ref_ptr<osg::Group> root = new osg::Group;		
		root->addChild(node1);//内存泄漏
		root->addChild(node2);//野指针崩溃
		root->addChild(node3);//正确。
		osgViewer::Viewer* viewer = new osgViewer::Viewer;
		viewer->setSceneData(root);
		return viewer->run();
}

上面代码的说明:

3.1、create1()函数:

osg::Node* create1()
{
  	  osg::Node* nodeTemp = osgDB::readNodeFile("cow.osg");
     return  nodeTemp ;
}

nodeTemp对象为osg::Node的指针对象,是局部变量。create1()函数外面,nodeTemp对象就析构了,析构的是指针,new的空间没有释放,出现内存泄漏。

3.2、create2()函数:

osg::Node* creat2()
{
   	osg::ref_ptr<osg::Node> nodeTemp = osgDB::readNodeFile("cow.osg");
  	 return nodeTemp.get();
}

nodeTemp对象为osg::ref_ptr< osg::Node >的对象,是局部变量。具体点 nodeTemp是osg::ref_ptr的对象, < osg::Node >为给模板类传递的类。
create2()函数外面,nodeTemp对象就析构了,调用osg::ref_ptr类的析构函数。

/*析构函数,在此析构函数中,首先调用unref函数使得管理的指针中的引用计数减一,
        之后将当前指针置空。*/  
        ~ref_ptr() { if (_ptr) _ptr->unref();  _ptr = 0; }

其中,_ptr->unref(); 函数,在引用计数减一后,判断是否为零,为零进行delete操作清理内存。

 /*unref()方法,在此方法中首先对_refCount进行--运算,之后再判断经过—运算之后的
        引用计数是否等于0,若等于0(意味着没有对象引用此段内存空间,可以被释放)
        则将needDelete赋值为true。在函数最后,对needDelete进行判断,如果需要被释放,
        则调用signalObserversAndDelete(true,true)方法,在这个函数中将会具体执行对对象的删除。*/
        inline int unref() const;

create2()函数外面,nodeTemp对象就析构了,这个对象的成员变量 _ptr 指针指向的new的空间也已经delete了。所以,这时node2对象中的成员变量 _ptr 指针指向的空间已经释放了,是野指针。会造成系统崩溃。

3.3、create3()函数:

osg::Node* creat3()
{
   osg::ref_ptr<osg::Node> nodeTemp = osgDB::readNodeFile("cow.osg");
   return nodeTemp.release();
}

其中,release函数,调用的是unref_nodelete()函数。

/*对引用计数执行–1 运算。不检查引用计数是否为零,
        不进行内存空间delete或其它方式的内存释放*/       
        int unref_nodelete() const;

所以,create3()函数外面,nodeTemp对象就析构了,这个对象的成员变量 _ptr 指针指向的new的空间却没有delete,没有释放。所以,node3的成员变量 _ptr 指针指向的空间不是野指针。对node3赋值时候,对引用计数又加一。main函数退出时,引用计数为零时,可以检测删除。所以,也没有内存泄漏。完美؏؏☝ᖗ乛◡乛ᖘ☝؏؏

4、额外推荐

最后,推荐C++野指针实例的教程,说明的特别清楚明白十分经典。《高质量C/C++编程》(林锐博士)
本来 想放链接的,但是发现我竟然没有备份,而且,现在不好找了!原来哪里都有啊!
这里面有部分关于野指针的情况,最后的考卷和解答说的特别清楚明白。

5、最后

学习的时候查了很多别人写的。要是还有不明白的,强烈推荐下面这个blog,对于初学者来说讲得最清楚明白。
其中,有一个错误重复出现。就是他总说“内存泄漏”,但是我认为他很多说“内存泄漏”的地方都是野指针。内存泄漏的错误可能最后不用改,因为程序不大内存很多,但是野指针就是会程序崩溃的。其它的地方都很好。推荐推荐。
https://blog.csdn.net/u010133496/article/details/40215471

我感觉看这一个blog,osg智能指针的东西就全部明白了。我写的这个blog也是90%以上都是这里面的内容。我只是结合我应用的osg源码版本对代码部分进行了修改。按照我自己的理解方式和思维习惯记录的。为了以后忘记了速查用的。最近简直太能够忘东西了!(T_T)(T_T)

下面的blog是转载上面的blog,完全一样,但是我百度的时候,下面的链接排在前面,因为。。。代码都是黑底的,看着更加舒服!
https://blog.csdn.net/trevor_k/article/details/98472292

-------------------完结,好长,我好伟大!-------------------

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值