OSG智能指针之强指针与弱指针

使用OSG的人都知道OSG的内存管理方式采用了智能指针,通过智能指针的方式让OSG自己处理对象的销毁工作。在OSG中有两个智能指针类型,一个就是我们再熟悉不过的ref_ptr,另外一个可能很多人不一定经常用,但确实非常好的observer_ptr。这两种类型都是作为智能指针而出现的,那他们到底有什么区别?为什么会出现两个类似的东西?下面我们慢慢揭开它们的面纱。

其实它们是两种智能指针类型,一种是我们熟悉的强指针类型(strong pointer),一种是弱指针类型(weak pointer),ref_ptr就是强指针类型,observer_ptr是弱指针类型,强指针类型ref_ptr和一般意义的智能指针类型相同,通过引用计数来记录有多少对象在使用该对象,当没有任何对象使用该对象时该对象自动销毁,而observer_ptr也指向一个对象,但它没有引用计数功能,它仅仅记录该对象的地址,当该对象在其它地方被销毁后,通过observer_ptr获得的对象地址将为空。为什么称之为弱指针,本人不知道这个概念的提出者是如何想的,个人感觉可能就是因为它仅仅是对对象地址的一个记录,不参与对象的生存期管理,所以称之为弱。不知道您是否这样认为,反正我这样认为了,哈哈。

说到这里我们应该会有疑问,为什么还要多个弱智能指针呢?这不成了多个弱智吗?一个ref_ptr不就够了吗?我说说我的看法,不知道是不是准确,仅仅代表个人的观点,如果我们只用ref_ptr,那就意味着在任何情况下只要我们需要引用某个对象,就要增加它的引用计数,不用了就减少引用计数,我们要明确的第一个问题就是,如果对象数量特别大,频繁的增加引用计数和减少引用计数本身会降低一定的新能,这一点应该勿容置疑,另外,在很多情况下我们只是需要对对象进行引用以便在对象生存期内方便访问,而如果使用ref_ptr增加它的引用计数后也就改变了该对象的生存期,对象的生存期需要我们自己来通过额外的手段进行控制,这样必然增加系统的复杂性,比如我们在系统中创建了若干飞机,同时系统中有一个对所有飞机进行统一管理的飞机管理器,正常的逻辑是当飞机爆炸后我们要销毁该飞机对象,如果我们在飞机管理器中使用ref_ptr,飞机在别的地方全部解除引用后飞机对象也不会自动销毁,因为飞机管理器中还存在对其的引用,必须借助一定判断手段人为将该飞机对象从飞机管理器解除引用才将飞机对象引用计数降为0进而销毁该对象。这样以来我们是不是做了一件出力不讨好的事?如果用observer_ptr,我们将不受这种困扰,因为它不干涉对象的生存期,而且不会因为对引用计数的操作降低效率。这就是个人理解的为什么会出现弱智能指针的原因,可能不准确或不够全面,欢迎高人修正补充,这里权当抛砖引玉。

接下来我们就看看observer_ptr如何实现的对对象的引用,如何做到当该对象被销毁后observer_ptr会知道该情况。其实很简单,通过名字我们就能猜出来这里肯定使用了观察者模式,当对象销毁后自动通知该观察者。下面我们通过代码来看看是不是这样。

首先我们要看的一定是该智能指针的构造函数,它有若干构造函数,我们就用最常用也最典型的来看,其它的大同小异而已。

observer_ptr(T* rp)

{

_reference = rp ? rp->getOrCreateObserverSet() : 0;

_ptr = (_reference.valid() && _reference->getObserverdObject()!=0) ? rp : 0;

}

这和ref_ptr一样通过一个原始对象进行构造,在构造函数里做了一件事,就是对成员变量的初始化,成员变量一共两个,一个就是称之为观察者集合的对象,另外一个和ref_ptr一样就是该智能指针引用的原始对象的指针。这里的重点是那个观察者集合的成员,它其实就是对对象的一个观察者,当对象在别的地方被销毁后,该观察者会被通知到。

其实上面的rp、_ptr和_reference->getObserverdObject()是一个东西,我们想想,如果不一样是不是就完蛋了?因为我们要引用和观察的必须是一个东西!任何一个从Referenced类继承下来的子类都可以拥有一个观察者集合,一个观察者集合观察一个对象,一个集合可以拥有若干观察者,这里不涉及到观察者集合中的观察者,只是涉及到被观察的对象,所以我们为了防止干扰就不说它了,有兴趣的可以看代码。rp->getOrCreateObserverSet()就是给该智能指针引用的对象创建一个观察者集合,在创建观察者集合时,该对象就自然告诉该观察者集合观察自己了,不信?代码为证:

ObserverSet* Referenced::getOrCreateObserverSet() const

{

......

ObserverSet* newObserverSet = new ObserverSet(this);

......

}

到这你应该明白刚才说的rp、_ptr和_reference->getObserverdObject()怎么是一个东西了吧。

好了,我们已经明白observer_ptr是如何观察其引用的对象了,那对象销毁的时候它是怎么知道的呢?什么?你不信它能知道?不管你信不信,我反正信了。看代码:

void Referenced::signalObserversAndDelete(bool signalDelete, bool doDelete) const

{

......

if (observerSet && signalDelete)

{

observerSet->signalObjectDeleted(const_cast<Referenced*>(this));

}

......

}

void ObserverSet::signalObjectDeleted(void* ptr)

{

......

_observedObject = 0;

}

看到了吧,当它引用的对象销毁时会告诉这个观察者集合:你观察的对象没有啦:_observedObject = 0;

到这里,一去一返就接上了。快完了,别着急,我们再看一行代码:

inline T* get() const { return (_reference.valid() && _reference->getObserverdObject()!=0) ? _ptr : 0; }

看到什么了?如果对象销毁了它返回的是什么?空!你答对了,别忘了它不增加引用对象的引用计数,不修改引用的对象的生存期,这就意味着你引用的对象在你使用时可能已经在别的地方销毁了,所以用的时候一定别忘了判断一下,要不很容易“嘣!”的一声。

完了吧?哦,不行,还有一点,如果我使用的时候,在使用过程中,对象在别的线程被销毁了怎么办?哎呀,确实是啊,这该怎么办?岂不是还得用ref_ptr,没错,这时候就要用ref_ptr了,否则你正在房顶上揭瓦呢,下面那王八蛋把梯子给你搬走了你摔死了。用ref_ptr!但不是让你舍弃observer_ptr不用只用ref_ptr哦,看代码我告诉你用什么:

bool lock(ref_ptr<T>& rptr) const

{

if (!_reference)

{

rptr = 0;

return false;

}

Referenced* obj = _reference->addRefLock();

if (!obj)

{

rptr = 0;

return false;

}

rptr = _ptr;

obj->unref_nodelete();

return rptr.valid();

}

{

rptr = 0;

return false;

}

Referenced* obj = _reference->addRefLock();

if (!obj)

{

rptr = 0;

return false;

}

rptr = _ptr;

obj->unref_nodelete();

return rptr.valid();

}

这次你明白如果你意识到会有这种隐患的时候该怎么做了?我就不翻译了,看两个官方注释:

该函数的注释是:

/**

* Assign the observer_ptr to a ref_ptr. The ref_ptr will be valid if the

* referenced object hasn‘t been deleted and has a ref count > 0.

*/

还有对observer_ptr的注释:

* If you are in any doubt about whether it is safe to access the object safe then use the

* ref_ptr<> observer_ptr<>.lock() combination. */

全部完毕!

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-05 12:30:31

OSG智能指针之强指针与弱指针的相关文章

Objective-C(十六、内存管理,自动释放池,ARC,强指针,弱指针,方法族)——iOS开发基础

结合之前的学习笔记以及参考<Objective-C编程全解(第三版)>,对Objective-C知识点进行梳理总结.知识点一直在变,只是作为参考,以苹果官方文档为准~ 十六.内存管理相关知识(二) 1.autorelease,自动释放机制 - (instancetype)autorelease; (1)自动释放池的创建 iOS5.0之前 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //进行一系列操作 //此处不可以使用

Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6786239 Android 系统的运行时库层代码是用C++来编写的,用C++来写代码最容易出错的地方就是指针了,一旦使用不当,轻则造成内存泄漏,重则造成系统崩溃.不过系统为 我们提供了智能指针,避免出现上述问题,本文将系统地分析Android系统智能指针(轻量级指针.强指针和弱指针)的实现原理. 在使用C++来编写代码的过程中,指针使用不当造成

OSG 智能指针陷阱

先看下这个代码,有什么问题: #include <osg/Group> #include <osg/Node> #include <osg/Geode> osg::Geode *geode= NULL; osg::ref_ptr<osg::Group> root = NULL; void createNode(){ geode = new osg::Geode; geode->setName("Hello"); root = new

Android中强指针和弱指针

因为Android中很多地方代码是用C++编写,为了能够保证C++中指针能够被正确的释放,于是Android引入了其实在C++中已经有的智能指针技术: 智能指针技术的实质就是:记录引用某一个对象的次数,一旦检测到次数为0,这时就自定将此对象所占内存释放. 简单的的智能指针技术因为不能解决对象循环引用的问题:a引用b:b引用a,这样的情况下使用简单的智能指针技术无法解决,故引入强指针和弱指针: 其实完全可以把强弱指针看做c语言中的一个指向对象的地址(为了便于理解),不过区别在于强指针可以操作对象,

智能指针 与 oc中的指针

智能指针 与 oc中的指针 智能指针的原理及实现 当类中有指针成员时,一般有两种方式来管理指针成员:一是采用值型的方式管理,每个类对象都保留一份指针指向的对象的拷贝:另一种更优雅的方式是使用智能指针,从而实现指针指向的对象的共享. 智能指针(smart pointer)的一种通用实现技术是使用引用计数(reference count).智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针. 每次创建类的新对象时,初始化指针并将引用计数置为1:当对象作为另一对象的副

OC学习笔记 ARC 强指针和弱指针 内存管理

强调一些概念 类:是一种结构,它表示对象的类型,对象引用类来获取和本身有关的各种信息,特别是运行什么代码来处理每种操作. 对象:是一种结构,它包含值和指向其类的隐藏指针. 实例:对象的另一种称呼. 消息:是对象可以执行的操作,用于通知对象去做什么.对象接收消息后,将查询对应的类,以便查找正确的代码来运行. 方法:是为响应消息而运行的代码,根据对象的类,消息可以调用不同的方法. 接口:是对象的类应该提供特殊的特性的描述. 用法如:@property (attribute1,attribute2)

IOS 强指针(strong)和弱指针(weak)

// strong 强指针        // weak 弱指针        // ARC, 只要对象没有强指针就会自动释放        // OC中默认都是强指针

C语言指针2(空指针,野指针)

//最近,有朋友开玩笑问 int *p  *是指针还是p是指针还是*p是指针,当然了,知道的都知道p是指针 //野指针----->>>指没有指向一个地址的指针(指针指向地址请参考上一篇文章) //空指针---->>指向空(null)的指针就是空指针 //指针的其他用法,指针可以指向指针,指针可以进行+ - * /运算 /* 特别注意,各个编译器都不相同,这里有一种错误写法,如: int *p,int a=10,b=20; p=&b; *p = &a;    

C++C++ 指针(二)--c++ 指针(二)--c++

一.内存管理:new和delete 1.new操作符:从操作系统获得内存块,并返回该内存块的首地址. delete操作符:将new申请的内存返还给操作系统. 开始一个简单的例子: #include <iostream> #include<cstring> using namespace std; int main() {     char* str="it is a good job!";     int len=strlen(str);     char* p