C++引用计数思想--利用引用计数器自定义String类

什么是引用计数? 

最直观的垃圾收集策略是引用计数。引用计数很简单,但是需要编译器的重要配合,并且增加了赋值函数 (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;
}

运行结果如下:



*本文代码均经过测试





  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值