【转载】C++应用引用计数技术

原帖:http://www.cnblogs.com/chain2012/archive/2010/11/12/1875578.html

因为Windows的内核对象也运用了引用计数,所以稍作了解并非无用。

引用计数可以让多个对象共享一个数据,而且免除了跟踪控制权的负担,让对象自己管理自己,当再没有被使用时可以自动删除,也算是一种简易的垃圾回收机制。

另一方面,如果有N多个相同的对象:○=○=○=○=...=○=○ 这样的做法是臃肿且无聊的,所以一个好的做法就是让对象可以共享这一个数据。既可以节省内存,又可以提高效率让程序负担更少,不用构造和析构这个值对象的拷贝了。

1 String a, b, c, d, e;2 a=b=c=d=e="hello";

1 String& String::operator=(const String &rhs)2 {3     if (data==&rhs) return *this;  //防止自我赋值4      delete [] data;5     data = new char[strlen(rhs.data)+1)];6     strcpy(data, rhs.data);7     return *this;8 }

用图显示的话,即:

当a被赋予了另外的值,a="world"; 这时候不能删除这个Hello,应外仍然存在bcde,4个对象在共享这个数据;另外,当只有1个对象x在用这个Hello,而x已经超过了其生存期,没有其他对象指向这个Hello的时候,我们需要删除这个Hello确保不发生资源泄漏。这也就意味着引入引用计数后,图将改变成这样:

  • 实现引用计数

应该是每一个String值对应一个计数数值,而不是String对象对应一个引用计数。接下来,新建一个嵌套类StringValue来保存计数和其跟踪的值。

String.h

 1 #include <string> 2  3  class String { 4  public: 5     String(const char *initValue=""); 6     String& String::operator=(const String &rhs); 7  8  private: 9     // StringValue的主要目的是提供一个空间将一个特别的值和共10     // 享此值的对象的数目联系起来11     struct StringValue    //嵌套类,引用计数12     { 13         int refCount; //计数数值14         char *data;15         StringValue (const char* initValue);16         ~StringValue();17     };  18     StringValue *value;19 };

String.cpp

 1 #include "String.h" 2  3 String::StringValue::StringValue(const char* initValue) 4     :refCount(1) 5 { 6     data = new char[strlen(initValue)+1]; 7     strcpy(data, initValue); 8 } 9 10 String::StringValue::~StringValue()11 {12     delete [] data;13 }14 15 String::String(const char *initValue)16     :value(new StringValue(initValue))17 {18 19 }

而这样做通常会产生一个问题,

String s1("More Effective C++");

String s2("More Effective C++");

将会变成这样的数据结构:

想办法改进一下:

控制副本的简单实现

 1 list<string> String::StringValue::independObj; //独立对象 2  String::StringValue::StringValue(const char* initValue) 3     :refCount(1) 4 { 5     typedef list<string>::iterator lsp; 6     lsp p = find(independObj.begin(), independObj.end(), string(initValue)); 7     if (p==independObj.end()||independObj.empty()) 8     {//未找到对象,新建 9          data = new char[strlen(initValue)+1];10         strcpy(data, initValue);11         independObj.push_back(string(data));12     }13     else14    {15          // do something...16      }17 }

接下来看下String类的拷贝构造函数

String::String(const String& rhs)     : value(rhs.value) {     ++value->refCount; } 

当这样构造2个对象:

String s1("More Effective C++");

String s2(s1);

就会产生这样的数据结构,其代价是非常低廉的,省去了新对象的构造(不必分配新内存和把内容拷贝到这块内存中)和之后的析构(不必释放那块内存),仅仅是使计数+1和拷贝了下指针

拷贝构造函数之后看下析构函数

String::~String(){    if (--value->refCount == 0)    {        delete value;    }}

即,当被引用的对象还有其他共享对象时,仅把计数-1;而当没有其他共享对象时,才彻底将引用对象析构掉。接着,是重载赋值操作符,稍微有些复杂

String& String::operator=(const String &rhs){    if (value == rhs.value) //赋值的是其本身        return *this;       //什么也不做    if (--value->refCount == 0)  //如果只有当前对象在共享那个数据        delete value;            //则删除掉,因为即将被赋予新的引用。不是的话,仅将计数-1    value = rhs.value;  //赋值操作    ++value->refCount;  //计数器+1    return *this;}

  • 写时拷贝

const版本的下标操作仅仅是只读的,不会对引用对象做出修改

const char& String::operator[](int index) const  //const版本{    //需下标溢出检查    return value->data[index];}

需要考虑的是非const版本的下标操作,因为C++编译器无法告诉我们非const的operator[]是会被用来读还是写操作。所以我们保守地认为所有的操作都是“写”的。

char& String::operator[](int index)  //非const版本{    if (value.refCount>1) //如果引用对象不止一个    {        --value.refCount; //计数减一,相当于把这个引用删除了        value = new StringValue(value->data); //重新申请一份新的拷贝    }    return value->data[index];}

这个思想就是:“与其他对象共享的一个值直到写操作时才拥有自己的拷贝”。即,lazy原则的特例。

  • 指针、引用与写时拷贝

在大部分情况下都能满足以上的应用,可是唯一情况却颇为棘手,比如

String s1("More Effective C++");

char* p=&s1;

String s2 = s1;

拷贝构造函数让s2和s1共享这个对象,这时候的数据结构为

如果写下这样一句: p[1]=‘X‘; //将同时修改s1和s2的内容!String 的拷贝构造函数无法检测出s1拥有指向StringValue指针的存在。该问题的一个解决方法就是:在每个StringValue中增加一个标志,表示该对象是否可以被共享。在最初是ture状态,而在调用了非const的operator[]之后则设置成false,且之后永远置于false状态。

追加共享标志位的String

  • 带引用计数的基类

引用计数不仅运用在字符串类上,只要是多个对象共享相同值的类都可以。

构建一个基类(RCObject),任何需要引用计数的类都必须继承自此类。由RCObject类封装引用计数功能。

RCObject.h

RCObject.cpp

  • 自动引用计数处理

RCObject类给了我们一个存储引用计数的地方,并提供了成员函数供我们操作引用计数,但调用这些函数的动作还必须被手工加入其它类中。仍然需要在String的拷贝构造函数和赋值运算函数中调用StringValue的addReference和 removeReference函数。这很笨拙。

StringValue *value; 必须操作StringValue对象的refCount字段。是否能够让指针自身检测发生复制拷贝,赋值操作,析构操作此类事件,而对于计数经行修改的操作呢?答案是否定的。代替的方法就是利用智能指针。

【分析】

开始看函数式treap的时候看到的...

话说函数式treap消耗的内存那么大吗?还要用引用计数。

表示只用内存池。

顺便附上一份网上函数式treap模板...

http://ideone.com/kbSjPp

时间: 2024-11-13 14:35:26

【转载】C++应用引用计数技术的相关文章

Android的引用计数(强弱指针)技术及一些问题

Android C++框架层的引用计数技术 C++中对指针的使用时很头疼的事情,一个是经常会忘记free 指针,造成内存泄露,另外一个就是野指针问题:访问已经free掉的指针.程序debug工作的相当大部分,都是花费在这.Android中通过引用计数来自动管理指针的生命周期,动态申请的内存将会在不再需要时被自动释放(有点类似Java的垃圾回收),不用程序员明确使用delete来释放对象,也不需要考虑一个对象是否已经在其它地方被释放了,从而使程序编写工作减轻不少,而程序的稳定性也大大提高. And

memcached源码分析-----item锁级别与item引用计数

        转载请注明出处:http://blog.csdn.net/luotuo44/article/details/42913549 锁级别: 从前面的<扩展哈希表>知道:哈希表进行扩展时,有一个专门的线程负责将item数据从旧哈希表迁移到新哈希表(由此,也称这个线程为迁移线程).此外,还有一些worker线程会时不时访问item(包括插入.删除和获取).这些线程的对item所做的操作基本上都是互斥的,必须加锁控制. 如果只使用一个锁,抢到该锁才能使用哈希表,没有抢到则不能使用.这样的

深拷贝 浅拷贝 引用计数

;深拷贝 浅拷贝 深拷贝(成员用到了指针存储空间地址)每个对象的成员都有自己独立的成员内存地址空间,造成了浪费 浅拷贝,把原对象的指针也直接拷贝过来我还是用的这一片空间,但是析构会有重复释放问题,解决重复释放用引用计数记录这个类产生了多少对象,析构的时候--引用计数就可以了 计数为0   delete 这块内存空间 ;引用计数技术 优点:所有对象共享同一片空间,间接的达到了对象间的数据共享 缺点:一旦一个对象改变了这片内存,那么所有的对象都受到影响 ;写时拷贝技术 当对象需要操作这一块空间存放数

Cocos2d之&ldquo;引用计数&rdquo;内存管理机制实现解析

一.引言 本文主要分析cocos2d游戏开发引擎的引用计数内存管理技术的实现原理.建议读者在阅读本文之前阅读笔者之前一篇介绍如何使用cocos2d内存管理技术的文章--<Cocos2d之Ref类与内存管理使用详解>. 二.相关概念 引用计数 引用计数是计算机编程语言的一种内存管理技术,是指将资源(对象.内存或者磁盘空间等)的被引用计数保存起来,当引用计数变为零时就将资源释放的过程.使用引用计数技术可以实现自动内存管理的目的. 当实例化一个类时,对象的引用计数为1,在其他对象需要持有这个对象时,

CString是否使用了引用计数

Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源 问题: CString是否使用了引用计数? 答案是肯定的.平常使用的时候确实没有查觉这方面,包括我们可以把CString强制转为TCAHR*来使用,效果也都正常. CString强制转为TCAHR*为什么能正常,原因是什么呢? 原因是单纯从数据结构上来看,它仅有一个变量: m_pszData 但它动态申请的内存结构是这样的 [CStringData结构][存储字符串] 而m_pszData

基于引用计数的智能指针

编程语言中实现自动垃圾回收机制方式有好几种,常见的有标记清除,引用计数,分代回收等. C++需要手动管理垃圾,可以自己实现一个智能指针.最简单的是引用计数的思路 template <class T> class SmartPointer { T* obj; unsigned int* count; SmartPointer(T* ptr) { obj = ptr; count = new int; *count = 1; } SmartPointer(SmartPointer &p)

智能指针的实现--使用引用计数实现以及原理

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

Python内存管理及引用计数

作为一门动态语言,python很重要的一个概念就是动态类型,即对象的类型和内存占用都是运行时确定的.(Why?)运行时,解释器会根据语法和右操作数来决定新对象的类型.动态类型的实现,是通过引用和对象的分离达到的.对象是存放在内存中的数据实体,而引用(reference)可以理解成一个封装好的指向对象的指针.不过操作更加方便和安全.就像C++中的引用是对指针操作的简化和封装一样.在python中,内存的管理,即分配与回收,都是由python解释器来做的,程序员不需要关心太多.或者,也可以把引用理解

《Objective-C 高级编程》干货三部曲(一):引用计数篇

总结了Effective Objective-C之后,还想读一本进阶的iOS书,毫不犹豫选中了<Objective-C 高级编程>: 这本书有三个章节,我针对每一章节进行总结并加上适当的扩展分享给大家.可以从下面这张图来看一下这三篇的整体结构: 注意,这个结构并不和书中的结构一致,而是以书中的结构为参考,稍作了调整. 本篇是第一篇:引用计数,简单说两句: Objective-C通过 retainCount 的机制来决定对象是否需要释放. 每次runloop迭代结束后,都会检查对象的 retai