什么是引用计数?
最直观的垃圾收集策略是引用计数。引用计数很简单,但是需要编译器的重要配合,并且增加了赋值函数 (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;
}
运行结果如下:
*本文代码均经过测试