原帖: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 else
14 {
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

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

  1. C++ 引用计数技术及智能指针的简单实现

    一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...

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

    Android的引用计数(强弱指针)技术及一些问题 分类: Android 2013-06-07 18:25 844人阅读 评论(4) 收藏 举报 目录(?)[+] Android C++框架层的引用 ...

  3. 【M29】引用计数

    1.引用计数这项技术,是为了让等值对象对象共享同一实体.此技术的发展有两个动机:a.记录堆上分配的对象,是垃圾回收机制的简单原理:b.节省内存,多个对象具有相同的值,存储多次很笨.速度更快,等值对象避 ...

  4. python 引用计数

    转载:NeilLee(有修改)   一.概述 要保持追踪内存中的对象,Python使用了引用计数这一简单的技术. sys.getrefcount(a)可以查看a对象的引用计数,但是比正常计数大1,因为 ...

  5. 【Python】引用计数

    一.概述 要保持追踪内存中的对象,Python使用了引用计数这一简单的技术. 二.引用计数的增减 2.1 增加引用计数 当对象被创建并(将其引用)赋值给变量时,该对象的引用计数被设置为1. 对象的引用 ...

  6. iOS开发--引用计数与ARC

    以下是关于内存管理的学习笔记:引用计数与ARC. iOS5以前自动引用计数(ARC)是在MacOS X 10.7与iOS 5中引入一项新技术,用于代替之前的手工引用计数MRC(Manual Refer ...

  7. ATL是如何实现线程安全的引用计数和多线程控制的

    ATL是如何实现线程安全的引用计数和多线程控制的 正如标题所示,这是我经常被问到的一个问题,而每次我都从头开始给人说一次,其实说来过程理解起来的确有点复杂. 我们的每一个ATL Server Obje ...

  8. 引用计数 vs. GC

    内存管理问题 内存管理是编程过程中的一个经典问题,早期在 C 语言时代,几乎都靠 malloc/free 手动管理内存.随着各个平台的发展,到现在被广泛采用的主要有两个方法: 引用计数 (ARC,Au ...

  9. Python 对象的引用计数和拷贝

    Python 对象的引用计数和拷贝 Python是一种面向对象的语言,包括变量.函数.类.模块等等一切皆对象. 在python中,每个对象有以下三个属性: 1.id,每个对象都有一个唯一的身份标识自己 ...

随机推荐

  1. 去掉eclipse js 错误提示

    1.去掉项目目录底下的.project文件中的以下部分:<buildCommand>      <name>org.eclipse.wst.jsdt.core.javascri ...

  2. 获取css的属性值

    # -*- coding:utf-8 -*- """ 在元素上执行双击操作 """ from selenium import webdriv ...

  3. Python操作Excel_随机点菜脚本

     背景:     中午快餐,菜单吃了个遍,天天纠结于不知道点啥菜.      想起读书考试时,丢纸团选答案,于是用python写个随机点菜脚本玩玩. 功能:      菜单为Excel,一个Sheet ...

  4. SqlServer:CTE函数处理递归(WITH语法)

    我们在做分类处理的时候,总会遇到递归的处理,比如说地区就是一个例子,中国--北京--西城区,我们可以把这样的信息存储在一个数据表中,用ParentID区分根节点和叶子节点.假如我们要做导航,得到了”西 ...

  5. Unity Kajiya Hair Shader Mod by Normals

    Shader "HairShader" { Properties { _MainTex ("Diffuse (RGB) Alpha (A)", 2D) = &q ...

  6. ACM1720_A+Bcoming(进制转换的新思路)——代码超少哟!

    using namespace std; int main() { int a,b; while(cin>>hex>>a>>b) { cout<<dec ...

  7. 实现自己的脚本语言ngscript之四:代码生成

    最近的进度 ngscript测试代码 function c1(a, b, c, d) { this.a = 1; this.b = new array(); this.b[0] = 1; this.b ...

  8. iOS高级工程师面试

    1. 你使用过Objective-C的运行时编程(Runtime Programming)么?如果使用过,你用它做了什么?你还能记得你所使用的相关的头文件或者某些方法的名称吗?  Objecitve- ...

  9. 获取文件路径 分类: WinForm 2014-07-25 14:27 103人阅读 评论(0) 收藏

    //可获得当前执行的exe的文件名. string str1 =Process.GetCurrentProcess().MainModule.FileName; //获取和设置当前目录(即该进程从中启 ...

  10. Android中fragment_main.xml文件里的组件获取的问题

    package com.dhy.phonedial; import android.app.Activity; import android.app.Fragment; import android. ...