C++引用计数思想--利用引用计数器自定义String类
2016-07-31 19:54
639 查看
什么是引用计数?
最直观的垃圾收集策略是引用计数。引用计数很简单,但是需要编译器的重要配合,并且增加了赋值函数 (mutator)的开销(这个术语是针对用户程序的,是从垃圾收集器的角度来看的)。每一个对象都有一个关联的引用计数 —— 对该对象的活跃引用的数量。如果对象的引用计数是零,那么它就是垃圾(用户程序不可到达它),并可以回收。每次修改指针引用时(比如通过赋值语句),或者当引用超出范围时,编译器必须生成代码以更新引用的对象的引用计数。如果对象的引用计数变为零,那么运行时就可以立即收回这个块(并且减少被回收的块所引用的所有块的引用计数),或者将它放到迟延收集队列中
com组件将维护一个称作是引用计数的数值。当客户从组件取得一个接口时,此引用计数值将增1。当客户使用完某个接口后,组件的引用计数值将减1.当引用计数值为0时,组件即可将自己从内存中删除。
在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。
--百度百科
为了将引用计数的思想实现出来,下文将采用自定义string类的方式说明引用计数的原理及工作方式。
我们自定义一个string类:_String ,它拥有一个私有成员,是引用计数器_String_rep类类型的指针。由于引用计数原理,我们知道所有的_String类如果指向同一字符串,或拷贝或赋值,它们将共同维护同一个String_rep类,共同维护它们的use_count(计数值)。
例如:
_String s1(“hello”); //内存中数据段存入字符串,s1指向属于它的引用计数器,引用计数器指向此字符串,计数值为1 _String s2(s1); //s2同样指向了上文中字符串,计数值+1; _String s3; //同理- s3 = s1;
上面的s1,s2,s3都指向了内存中数据段同一字符串,它们的计数值(use_count)等于3。
我们可以这样理解,我们用_String构造一个字符串,而_String类叫它的小弟_String_rep类去替他管理这个字符串,它小弟内部存在两个变量,一个帮它大哥记录了字符串在内存中的位置,一直指向该字符串。另一个记录了该字符串拥有的人数。也就是可能有别的大哥也有拥有权。所以,只有该字符串只属于它大哥一个人管理时,如果不想要了,就可以直接释放该字符串了。否则,把别的大哥拥有的东西释放掉了,那可就引发内存管理问题了。
模型如下图:
当use_count大于1时,管理use_count的类的析构只会使use_count--。只有use_count的值为0时,说明该内存只被一个对象管理,可以是释放该内存。所以说,引用计数的方法大大减小了内存空间的占用,提高了空间利用率。
自定义string实现如下:
#ifndef _STRING_USE_COUNT_H #define _STRING_USE_COUNT_H #include <iostream> #include <string.h> using namespace::std; class _String_rep{ //引用计数器类 friend class _String; public: _String_rep(const char *str_) : use_count(1) { //cout << "_String_rep create" << endl; //构造和析构函数中打印为测试使用 if(str_ == nullptr){ str = new char[1]; *str = '\0'; }else{ str = new char[strlen(str_) + 1]; strcpy(str, str_); } } ~_String_rep(){ //delete this 调用析构函数时,必须先将申请的字符串空间释放掉,然后“自杀” //cout << "_String_rep destroy" << endl; delete []str; str = NULL; } public: const char* get()const{ return str; } const unsigned int use_count_()const{ return use_count; } public: void increment(){ ++use_count; } void decrement(){ if(--use_count == 0) delete this; //当引用计数值为0时,delete this 会先调用析构函数,然后释放空间 } private: char *str; unsigned int use_count; }; class _String{ //自定义string类 friend ostream& operator<<(ostream&, const _String&); public: _String(const char *str_ = 0) : rep(new _String_rep(str_)){ //cout << "Create _String" << endl; } _String(const _String& rhs){ rep = rhs.rep; rep->increment(); } _String& operator=(const _String& rhs){ if(this != &rhs){ rep->decrement(); rep = rhs.rep; rep->increment(); } return *this; } ~_String(){ //cout << "Destroy _String" << endl; rep->decrement(); //对象析构使引用计数值减一,当计数值为0时,_String_rep类会进行析构处理。 } public: const char* get()const{ return rep->get(); } const unsigned int use_count()const{ return rep->use_count_(); } void tupper(){ //转换为大写字母,当需要改变一个对象时,需要进行深拷贝,防止修改其他string对象的值 if(use_count() > 1){ _String_rep* new_rep = new _String_rep(rep->str); rep->decrement(); rep = new_rep; } for(auto pch=rep->str; *pch!='\0'; ++pch){ //auto为C++11标准用法,让编译器自动推测pch类型 *pch -= 32; } } private: _String_rep *rep; }; ostream& operator<<(ostream& out, const _String& obj) //重载输出流 { out<<obj.get(); return out; } #endif下面为测试代码:
#include "string_use_count.h" int main() { _String sz1("hello"); cout << sz1.use_count() << endl; _String sz2(sz1); cout << sz1.use_count() << endl; _String sz3(sz2); cout << sz1.use_count() << endl; sz2.tupper(); cout << sz1 << endl; cout << sz2 << endl; cout << sz3 << endl; cout << sz1.use_count() << endl; cout << sz2.use_count() << endl; return 0; }
运行结果如下:
*本文代码均经过测试
相关文章推荐
- C++——std::string类的引用计数
- C++——std::string类的引用计数
- 【C语言】【面试题】C++中String类引用计数器的浅拷贝写法与深拷贝写法
- 【String类浅拷贝的实现】C++:String类引用计数浅拷贝的两种实现
- 【C语言】【面试题】C++中String类引用计数器的浅拷贝写法与深拷贝写法
- C++ 简易string类实现(三)-抽离引用计数
- C++ 简易string类实现(二)-引用计数
- 【String类浅拷贝的实现】C++:String类引用计数浅拷贝的两种实现
- C++应用系列:用智能指针shared_ptr中引用计数思想进行动态内存管理
- share_ptr 实现c++ 句柄引用计数
- C++编程思想3-利用C++进行文件操作封装C函数
- c++引用计数的本质
- String:利用引用计数进行实现
- C++引用计数实现垃圾回收机制
- C++学习笔记(13)——利用对象、引用、指针调用虚函数
- C++ 编程思想——引用和拷贝构造函数
- 求最大网络流的C++实现(利用广度优先遍历的思想)
- C++如何使用简单的引用计数
- 利用cacti和性能计数器实现自定义远程监控
- C++应用引用计数技术