【C++ techniques】Reference counting(引用计数)

Reference counting的两个技术动机:

  1. 为了简化heap objects周边的簿记工作,当对象运用了引用计数技术,一旦不再有任何人使用它,它便自动销毁自己,也因此,reference counting建构出垃圾回收机制的一个简单形式;
  2. 为了实现一种常识。如果许多对象有相同的值,将那个值存储多次是一件愚蠢的事,最好是让所有等值对象共享一份实值即可,不仅节省内存,也会使得程序速度加快,因为不再需要构造和析构同值对象的多余副本。

由于需要记录“目前共享同一实值”的对象个数,一个reference count(引用计数器)就此引入:

Reference Counting(引用计数) 的实现

我们需要为每一个字符串值准备一个引用计数,而不是为每一个字符串对象准备,暗示了“对象值”和“引用计数”之间有一种耦合关系:产生一个class,不但存储引用计数,也存储它们追踪的对象值:

class String
{
public:... //一般的String成员函数安排在这里
private:struct StringValue{...};	 //持有一个引用次数以及一个字符串值StringValue* value;   		 //String的值
};
//String的定义
class String
{
private:struct StringValue{int refCount;char* data;};StringValue(const char* initValue);~StringValue();...
};//StringValue提供一个地点:
//将"某特定值"以及"共享该值的String对象个数"关联起来。
String::StringValue::StringValue(const char* initValue):refCount(1)
{data = new char[strlen(initValue) + 1];strcpy(data,initValue);
}String::StringValue::~StringValue()
{delete [] data;
}
//String的成员函数从构造函数开始:
class String
{
public:String(const char* initValue = "");String(const String& rhs);...
};//传入char*创建一个新的StringValue对象
String::String(const char* initValue) : value(new StringValue(initValue));
{}
//String的复制构造函数很有效率
String::String(const String& rhs):value(rhs.value)
{++value -> refCount;
}//运用
String s1("More Effective C++");
String s2(s1);
//新生成的String对象与被复制的对象共享相同的StringValue对象

以上只需将指针复制一份,并将引用从次数加1,不需要分配和归还内存,效率更高。

//String的析构函数
class String
{
public:~String();
};
String::~String()
{if(--value->refCount == 0) delete value;//只有当唯一的用户被析构,String析构函数才销毁StringValue
}
//String的赋值操作符
class String
{
public:...String& operator = (const String&& rhs);
};String& String::operator = (const String& rhs)
{if(value == rhs.value)return *this;if(--value->refCount == 0)delete value;value = rhs.value;++value->refCount;return *this;
}

Copy-On-Write(写时才复制)

方括号操作符[ ]:它允许字符串中的个别字符被读取或被写;

class String
{
public:const char& operator[](int index) const;//针对const Stringschar& operator[](int index);			//针对non-const String...
};//const版本:只读动作;字符串内容不受影响
const char& String::operator[](int index) const
{return value->data[index];
}//non-const版本:
//可能用来读取一个字符串,也可能用来写一个字符串
String s;
...
cout << s[3];	//读取动作
s[5] = 'x';		//写入动作

为了安全实现出non-const operator[],我们必须确保没有其他任何“共享同一个StringValue”的String对象因写动作而改变:

char& String::operator[](int index)
{//如果本对象和其他String对象共享同一实值//就分割(复制)出另一副本供本对象自己使用if(value->refCount > 1)--value->refCount;  //将目前实值的引用频次减1,因为我们不再使用//为自己做一份新的副本value = new StringValue(value->data);//返回一个reference,代表我们这个“绝对不被共享”的//StringValue对象内的一个字符。return value->data[index];
}

Pointers,References,以及Copy-On-Write

为每一个StringValue对象加上一个标志(flag)变量,用以指示可否被共享:

//修改版
class String
{
private:struct StringValue{int refCount;bool shareable;			//新增此行char* data;};StringValue(const char* initValue);~StringValue();...
};String::StringValue::StringValue(const char* initValue):refCount(1),shareable(true)//新增此行
{data = new char[strlen(initValue) + 1];strcpy(data,initValue);
}String::StringValue::~StringValue()
{delete [] data;
}//复制函数的新行为
String::String(const String& rhs)
{if(rhs.value->shareable){value = rhs.value;++value->refCount;}else{value = new StringValue(rhs.value->data);}
}//Non-const operator[]是唯一将shareble设为false者:
char& String::operator[](int index)
{if(value->refCount > 1)--value->refCount;value = new StringValue(value->data);value->shareable = false;	//新增此行return value->data[index];
}

一个Reference-Counting(引用计数)基类

  • Reference-counting可用于字符串以外的场合;
  • 任何class如果其不同的对象可能拥有相同的值,都适用此技术。

产生一个base class RCObject,作为“reference-counted对象”之用。
任何class希望自动拥有reference counting能力,都必须继承自这个类:

//RCObject定义
class RCObject
{
public:RCObject();RCObject(const RCObject& rhs);RCObject& operator = (const RCObject& rhs);virtual ~RCObject() = 0;void addReference();void removeReference();void markUnshareable();bool isShareable() const;bool isShared() const;private:int refCount;bool shareable;
};//RCObject实现
RCObject::RCObject():refCount(0),shareable(true){}RCObject::RCObject(const RCObject& rhs):refCount(0),shareable(true){}RCObject& RCObject::operator = (const RCObject& rhs)
{	return *this; }RCObject::~RCObject(){}void RCObject::addReference()
{++refCount;
}void RCObject::removeReference()
{if(--refCount == 0)delete this;
}void RCObject::markUnshareable()
{shareable = false;
}bool RCObject::isShareable() const
{return shareable;
}bool RCObject::isShared() const
{return refCount > 1;
}//StringValue继承自RCObject
class String
{
private://StringValue的成员函数不再处理refCount字段,改由RCObject掌管struct StringValue: public RCObject{char* data;StringValue(const char* initValue);~StringValue();};...
};String::StringValue::StringValue(const char* initValue)
{data = new char[strlen(initValue) + 1];stpcpy(data,initValue);
}String::StringValue::~StringValue()
{delete [] data;
}

自动操作Reference Count(引用次数)

我们希望能够把这一系列调用动作移到一个可复用的class内,这么一来可以让诸如String之类的classes的作者不必操心reference counting的任何细节。

//下面这个模板用来产生智能指针指向reference-counted对象:
template<class T>
class RCPtr
{
public:RCPtr(T* realPtr = 0);RCPtr(const RCPtr& rhs);~RCPtr();RCPtr& operator = (const RCPtr& rhs);T* operator->() const;T& operator*() const;private:T* pointee;void init();
};//实现
template<T>
RCPtr<T>::RCPtr(T* realPtr = 0):pointee(realPtr)
{init();
}template<T>
RCPtr<T>::RCPtr(const RCPtr& rhs)pointee(rhs.pointee)
{init();
}template<T>
void RCPtr<T>::init()
{if(pointee == NULL)  return ;		if(pointee->isShareble() == false)pointee = new T(*pointee);pointee->addReference();
}

以上错误在于:init!
pointee = new T(*pointee)构造新的T对象,但是RCPtr在String类内部,T将是String::StringValue,所以我们必须为String::StringValue加上深复制函数:

class String
{
private:struct StringValue: public RCObject{StringValue(const StringValue& rhs);...};
};String::StringValue::StringValue(const StringValue& rhs)
{data = new char[strlen(rhs.data) + 1];strcpy(data,rhs.data);
}

把所有努力放在一起

template<class T>				//template class,用来产生
class RCPtr						//smart pointers-to-Tobjects;
{								//T必须继承自RCObject
public:RCPtr(T* realPtr = 0);RCPtr(const RCPtr& rhs);~RCPtr();RCPtr& operator = (const RCPtr& rhs);T* operator->() const;T& operator*() const;private:T* pointee;void init();
};class RCObject				//base class, 用于reference-counted objects
{
public:void addReference();void removeReference();void markUnshanreable();bool isShareable() const;bool isShared() const;protected:RCObject();RCObject(const RCObject& rhs);RCObject& operator = (const RCObject& rhs);virtual ~RCObject() = 0;private:int refCount;bool shareable;
};class String		//应用性class,这是应用程序开发人员接触的层面
{
public:String(const char* value = "");const char& operator[](int index) const;char& operator[](int index);private:struct StringValue: public RCObject{char* data;StringValue(const char* initValue);StringValue(const StringValue& rhs);void init(const char* initValue);~StringValue();};RCPtr<StringValue> value;
};//RCObject实现
RCObject::RCObject():refCount(0),shareable(true){}RCObject::RCObject(const RCObject& rhs):refCount(0),shareable(true){}RCObject& RCObject::operator = (const RCObject& rhs)
{ return *this; }RCObject::~RCObject(){}void RCObject::addReference()
{	++refCount; }
void RCObject::removeReference()
{if(--refCount == 0)delete this;
}void RCObject::markUnshanreable()
{ shareable = false; }
bool RCObject::isShareable() const
{return shareable;
}
bool RCObject::isShared() const
{return refCount > 1;
}//RCPtr实现
template<class T>
void RCPtr<T>::init()
{if(pointee == 0) return;if(pointee->isShareable == false)pointee = new T(*pointee);pointee->addReference;
}template<class T>	
RCPtr<T>::RCPtr(T* realPtr = 0):pointee(realPtr)
{ init();
}template<class T>	
RCPtr<T>::RCPtr(const RCPtr& rhs):pointee(realPtr)
{init(); 
}template<class T>
RCPtr<T>::~RCPtr()
{if(pointee)pointee->removeReference();
}template<class T>
RCPtr& RCPtr<T>::operator = (const RCPtr& rhs)
{if(pointee != rhs.pointee){if(pointee)pointee->removeReference();pointee = rhs.pointee;init();}return *this;
}template<class T>
T* RCPtr<T>::operator->() const
{ return pointee; }template<class T>
T& RCPtr<T>::operator*() const
{ return *pointee; }//String::StringValue实现
void String::StringValue::init(const char* initValue)
{data = new char[strlen(initValue) + 1];strcpy(data,initValue);
}String::StringValue::StringValue(const char* initValue)
{init(initValue);
}
String::StringValue::StringValue(const StringValue& rhs)
{init(rhs.data);
}String::StringValue::~StringValue()
{delete [] data;
}//String实现
String::String(const char* value = "")value(new StringValue(initValue)){}const char& String::operator[](int index) const
{return value->data[index];
}
char& String::operator[](int index)
{if(value->isShared()){value = new StringValue(value->data);}value->markUnshanreable();return value->data[index];
}

将Reference Counting加到既有的Classes身上

就像“StringValue只能实现细节,不需让String的用户知道”一样;
CountHolder也是实现细节,不需让RCWidget的用户知道。
事实上它是RCIPtr的实现细节,所以嵌套放入RCIPtr class内部。

RCIPtr实现如下:

template<class T>				
class RCIPtr						
{							
public:RCIPtr(T* realPtr = 0);RCIPtr(const RCIPtr& rhs);~RCIPtr();RCIPtr& operator = (const RCIPtr& rhs);const T* operator->() const;T* operator->();const T& operator*() const;T& operator*();
private:struct CountHolder: public RCObject{~CountHolder{ delete pointee; }T* pointee;};CountHolder* counter;void init();void makeCopy();
};template<class T>
void RCIPtr<T>::init()
{if(counter->isShareable == false){T* oldValue = counter->pointee;counter = new CountHolder;counter->pointee = new T(*pointee);}	pointee->addReference;
}template<class T>
RCIPtr<T>::RCIPtr(T* realPtr):counter(new CountHolder)
{counter->pointee = realPtr;init();
}template<class T>
RCIPtr<T>::RCIPtr(const RCIPtr& rhs):counter(rhs.counter)
{init();
}template<class T>
RCIPtr<T>::~RCIPtr()
{counter->removeReference;
}template<class T>
RCIPtr& RCIPtr<T>::operator = (const RCIPtr& rhs)
{if(counter != rhs.counter){counter->removeReference();counter = rhs.counter;init();}return *this;
}template<class T>
const T* RCIPtr<T>::operator->() const
{return counter->pointee;
}template<class T>
const T& RCIPtr<T>::operator*() const
{return *(counter->pointee);
}

总结

什么时候最适合reference counting技术:

  1. 相对多数的对象共享相对少了的实值;
  2. 对象实值的产生或销毁成本很高,或使它们使用许多内存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/101279.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity2017适配安卓12

测试版本为Unity2017.4.25f1 1.在自定义AndroidManifest.xml&#xff08;位于Assets\Plugins\Android\&#xff09;中添加android:exported"true" <?xml version"1.0" encoding"utf-8"?> <manifestxmlns:android"http://schema…

LuatOS-SOC接口文档(air780E)-- http - http 客户端

示例 -- 使用http库,需要引入sysplus库, 且需要在task内使用 require "sys" require "sysplus"sys.taskInit(function()sys.wait(1000)local code,headers,body http.request("GET", "http://www.example.com/abc").wait()log.info(…

探索 Redis 与 MySQL 的双写问题

在日常的应用开发中&#xff0c;我们经常会遇到需要使用多种不同类型的数据库管理系统来满足各种业务需求。其中最典型的就是Redis和MySQL的组合使用。 这两者拥有各自的优点&#xff0c;例如Redis为高性能的内存数据库提供了极快的读写速度&#xff0c;而MySQL则是非常强大的…

归一化与标准化的区别

文章目录 一、公式二、代码三、区别四、结论 一、公式 归一化 X ′ x − m i n m a x − m i n X { x-min \above{1pt} max-min} X′max−minx−min​ X ′ ′ X ′ ∗ ( m a − m i ) m i X X * (ma-mi) mi X′′X′∗(ma−mi)mi 标准化 X ′ x − m e a n s t d X…

常见的C/C++开源数值计算库

1. Eigen Eigen 是一个线性算术的 C 模板库&#xff0c;包括&#xff1a;vectors, matrices, 以及相关算法。功能强大、快速、优雅以及支持多平台。 2. Blaze Blaze 是一个开源、高性能 C 数学库&#xff0c;用于密集和稀疏算术。凭借其最先进的 智能表达式模板 实现&#x…

选择智慧公厕解决方案,开创智慧城市公共厕所新时代

在城市建设和发展中&#xff0c;公厕作为一个不可或缺的城市基础设施&#xff0c;直接关系到城市形象的提升和居民生活品质的改善。然而&#xff0c;传统的公厕存在着管理不便、卫生状况差、设施陈旧等问题。为了解决这些困扰着城市发展的难题&#xff0c;智慧公厕源头厂家广州…

mysql 信号量(Semaphores)信息

以下是show ENGINE innodb status内容中信号量&#xff08;Semaphores&#xff09;的信息 ---------- SEMAPHORES ---------- OS WAIT ARRAY INFO: reservation count 103550558 OS WAIT ARRAY INFO: signal count 1874952750 RW-shared spins 0, rounds 581651603, OS waits …

视频监控系统/视频汇聚平台EasyCVR如何反向代理进行后端保活?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

【问题总结】级数的括号可以拆吗?

问题 今天在做题的时候发现&#xff0c;括号这个问题时常出现。Σun&#xff0c;Σvn&#xff0c;和Σ&#xff08;unvn&#xff09;&#xff0c;两个级数涉及到了括号增删&#xff0c;Σ(un-1un)&#xff0c;级数钟的前后项的合并也涉及到了括号增删。 总结 添括号定理&…

利用异常实现短期hook

场景1 调用目标call 需要跳过某些判断或者函数 场景2 目标call 只需要部分指令执行 大概实现技术 设置线程上下文设置drX寄存器 实现硬件执行断点 主动调用目标call 通过硬件断点获取寄存器或修改eip 以上实现不改变crc且不通过驱动实现。只对当前执行线程有效&#xff…

Android Termux安装MySQL,并使用cpolar实现公网安全远程连接[内网穿透]

文章目录 前言1.安装MariaDB2.安装cpolar内网穿透工具3. 创建安全隧道映射mysql4. 公网远程连接5. 固定远程连接地址 前言 Android作为移动设备&#xff0c;尽管最初并非设计为服务器&#xff0c;但是随着技术的进步我们可以将Android配置为生产力工具&#xff0c;变成一个随身…

2023-10-11 LeetCode每日一题()

2023-10-11每日一题 一、题目编号 2512. 奖励最顶尖的 K 名学生二、题目链接 点击跳转到题目位置 三、题目描述 给你两个字符串数组 positive_feedback 和 negative_feedback &#xff0c;分别包含表示正面的和负面的词汇。不会 有单词同时是正面的和负面的。 一开始&…

docker概念

docker 容器&#xff1a;就是提供在多台主机上运行的应用程序相同的运行环境。 docker的概念 是开源的容器&#xff0c;是由Go语言开发的&#xff0c;是容器里面运用的工具&#xff0c;他是一个轻量级的虚拟机&#xff0c;可以利用docker在多台主机上创建与运行容器。 docke…

全国工商注册数据库的作用

随着经济的发展和市场竞争的加剧&#xff0c;越来越多的人开始关注公司的工商信息。这些信息不仅可以帮助人们了解公司的基本情况&#xff0c;还可以为投资者、合作伙伴、员工等提供决策依据。 工商数据库提供了全国范围内企业的基本信息。这些信息包括企业的名称、统一社会信用…

2018架构真题案例(四十九)

某文件采用多级索引结构&#xff0c;磁盘大小4K字节&#xff0c;每个块号4字节&#xff0c;那么二级索引结果时&#xff0c;文件最大。 A、1024 B、1024*1024 C、2048*2048 D、4096*4096 答案&#xff1a;B 霍尔三维结构以时间堆、&#xff08;&#xff09;堆、知识堆组成…

聊聊Android签名检测7种核心检测方案详解

聊聊Android签名检测总结与反思 背景&#xff1a; 这篇文章只讲Android端签名检测&#xff0c;安卓发展到现在&#xff0c;因为国内环境没有谷歌市场&#xff0c;所以很多官方推荐的Api没法使用 &#xff0c;所以国内的签名检测方式也是“千奇百怪” 。发展至今每种方法都有一…

【ARM Coresight 系列文章 9 -- ETM 介绍 1】

文章目录 ARM Coresight ETM 介绍1.1.1 ARM Coresight ETM 版本介绍1.1.2 ARM Coresight 常见术语1.2 ARM Coresight ETM 常用寄存器介绍1.2.1 TRCVIIECTLR(ViewInst Include-Exclude Control Register)1.2.2 TRCVISSCTLR(ViewInst Start/Stop Processing Element Comparator C…

【ElasticSearch】深入探索 DSL 查询语法,实现对文档不同程度的检索,以及对搜索结果的排序、分页和高亮操作

文章目录 前言一、Elasticsearch DSL Query 的分类二、全文检索查询2.1 match 查询2.2 multi_match 查询 三、精确查询3.1 term 查询3.2 range 查询 四、地理坐标查询4.1 geo_bounding_box 查询4.2 geo_distance 查询 五、复合查询5.1 function score 查询5.2 boolean 查询 六、…

WorkPlus一站式解决方案,助力企业构建统一门户系统

在信息爆炸的时代&#xff0c;企业管理面临着海量的数据和各类业务应用的复杂性。如何实现信息的井然有序、高效管理&#xff0c;成为企业发展的关键。WorkPlus作为领先的品牌&#xff0c;致力于打造统一门户系统&#xff0c;为企业提供全方位的服务和解决方案。本文将以知乎的…

常见的C/C++开源QP问题求解器

1. qpSWIFT qpSWIFT 是面向嵌入式和机器人应用的轻量级稀疏二次规划求解器。它采用带有 Mehrotra Predictor 校正步骤和 Nesterov Todd 缩放的 Primal-Dual Interioir Point 方法。 开发语言&#xff1a;C文档&#xff1a;传送门项目&#xff1a;传送门 2. OSQP OSQP&#…