初学c++引用计数器
2015-03-10 18:26
204 查看
以后会多写些本质论的文章,这样可以更好的引导自己去深入思考。
为什么要用引用计数?
场景:代码里X是一个非常重要的资源,模块A,B,C都有对其指针的引用,那么为了不出现内存泄露,常规的代码我们要怎么写?
1. A 模块用完X时,需要检查B,C是否还在引用X,如果B,C有一个在用,那么X只要删除掉对A的引用就可以了,
如果B,C对A都已经没有引用了,那么A需要删除对X的引用时,要同时清除掉X。
2.同样B,C在用完X时,也要重复做1里面的事情。
这样,代码将会多了许多的逻辑判断,同时模块B,C还需要对模块A提供查询是否在引用X的接口。
可以不这么恶心吗?
思考:能否A在释放X前,不需要知道是谁在引用X,只要知道有多少人在引用X?
回答:是的,如果只有我用X,那么我就直接删除,如果还有其他人用,我就什么都不管,只要去除掉对X的引用就可以了。 情况就会变的好一些。
那么如何做到能知道资源的引用次数那?
这就需要对每一个资源X的都有一个的计数,这个计数是和资源X的生命周期息息相关的。
那么如何来管理这个计数?怎么能在有模块引用资源X的时候,计数++,模块释放资源X的时候计数--那?
这个就有一定难度了,引用的方式会有很多种,比如 A = X; A.push_back(X); A[100] = X; ...
当然我们可以在代码里的每一处增加和释放资源引用的地方,都加上代码count++,count--;但这亦然很麻烦,维护成本很高。
如何简单些?
能否把对资源的引用和释放,看成是对一个类的拷贝和销毁来完成? 弄一个代理类,里面封装好计数和资源X。
把所有对资源X的引用都理解成对代理类的引用,对代理类的引用都理解成对代理类的拷贝,对代理的释放就是销毁代理类。
这样我们只需要在代理类的拷贝函数里count++ , 代理类的析构做count--就好了。
如果代理类的资源引用计数count 减为0,就看成所有人都释放了对资源X的引用,这时由代理类来完成对资源的销毁。
这样下来,事情就简化了很多。 所以学会抽象是多么重要的一件事情。
下面详解代码
为什么要用引用计数?
场景:代码里X是一个非常重要的资源,模块A,B,C都有对其指针的引用,那么为了不出现内存泄露,常规的代码我们要怎么写?
1. A 模块用完X时,需要检查B,C是否还在引用X,如果B,C有一个在用,那么X只要删除掉对A的引用就可以了,
如果B,C对A都已经没有引用了,那么A需要删除对X的引用时,要同时清除掉X。
2.同样B,C在用完X时,也要重复做1里面的事情。
这样,代码将会多了许多的逻辑判断,同时模块B,C还需要对模块A提供查询是否在引用X的接口。
可以不这么恶心吗?
思考:能否A在释放X前,不需要知道是谁在引用X,只要知道有多少人在引用X?
回答:是的,如果只有我用X,那么我就直接删除,如果还有其他人用,我就什么都不管,只要去除掉对X的引用就可以了。 情况就会变的好一些。
那么如何做到能知道资源的引用次数那?
这就需要对每一个资源X的都有一个的计数,这个计数是和资源X的生命周期息息相关的。
那么如何来管理这个计数?怎么能在有模块引用资源X的时候,计数++,模块释放资源X的时候计数--那?
这个就有一定难度了,引用的方式会有很多种,比如 A = X; A.push_back(X); A[100] = X; ...
当然我们可以在代码里的每一处增加和释放资源引用的地方,都加上代码count++,count--;但这亦然很麻烦,维护成本很高。
如何简单些?
能否把对资源的引用和释放,看成是对一个类的拷贝和销毁来完成? 弄一个代理类,里面封装好计数和资源X。
把所有对资源X的引用都理解成对代理类的引用,对代理类的引用都理解成对代理类的拷贝,对代理的释放就是销毁代理类。
这样我们只需要在代理类的拷贝函数里count++ , 代理类的析构做count--就好了。
如果代理类的资源引用计数count 减为0,就看成所有人都释放了对资源X的引用,这时由代理类来完成对资源的销毁。
这样下来,事情就简化了很多。 所以学会抽象是多么重要的一件事情。
下面详解代码
#include<iostream> #include<string> using namespace std; template<class T> class Handle { public: //复制构造函数 Handle(T *p = nullptr) :ptr(p), use(new size_t(1)) //初始化引用计数器为1 { } Handle(const Handle &h) :ptr(h.ptr), use(h.use) //复制构造函数,没调用一次+1 { ++*use; } Handle &operator=(const Handle &); //重载=操作符 引用计数器+1 T &operator*();//重载*运算符 T *operator->();//重载->运算符 const T &operator*() const; const T *operator->() const; ~Handle() //析构说明对象已经销毁,放弃当前引用资源,so -1 { rem_ref(); } private: T *ptr;//模拟公共资源 size_t *use;//计数器 void rem_ref() //引用计数器-1 { if (--*use == 0) { delete ptr; //删除资源 delete use; } } }; template<class T> Handle<T>& Handle<T>::operator = (const Handle &rhs) { ++*rhs.use; rem_ref();//如果开始引用了其他资源,由于又要引用其他资源,那么-1表示放弃引用当前资源 ptr = rhs.ptr; use = rhs.use; return *this; } template<class T> inline T& Handle<T>::operator*() { if (ptr) { return *ptr; } throw std::runtime_error("dereference of unbound Handle"); } template<class T> inline T *Handle<T>::operator->() { if (ptr) return ptr; throw std::runtime_error("access through unbound Handle"); } template<class T> const T &Handle<T>::operator->() const { if (ptr) return *ptr; throw std::runtime_error("dereference of unbound Handle"); } template<class T> const T *Handle<T>:: operator ->() const { if (ptr) return ptr; throw std::runtime_error("access through unbound Handle"); } int main() { Handle<int> hp(new int(42)); //这里 42表示资源 { Handle <int> hp2(hp);//调用复制构造函数进行初始化,将共同引用42 cout << *hp << " " << *hp2 << endl; *hp2 = 10;//公有资源变成10 } cout << *hp << endl; } //执行main 后引用计数器将变成0,删除资源和引用计数器
相关文章推荐
- 初学C++遇到的引用头文件问题
- c++ 初学 引用传递和对象传递
- 初学C++遇到的引用头文件问题
- 【C语言】【面试题】C++中String类引用计数器的浅拷贝写法与深拷贝写法
- 关于C++ 引用<初学写代码小记>
- C++引用计数思想--利用引用计数器自定义String类
- c++初学之引用
- 【C语言】【面试题】C++中String类引用计数器的浅拷贝写法与深拷贝写法
- 【C++】引用计数器简单示例
- C++ 引用计数器
- C++中引用(&)的用法和应用实例
- c++中引用和指针的区别
- 【C++】学习笔记四十——引用变量
- C++_引用
- [C++]指针和引用(四)
- C++文件头,命名空间,new和delete,内联函数,引用,函数重载,构造函数和析构函数,深拷贝和浅拷贝,explict,this指针
- C++: 引用和地址运算符
- c++引用和指针的实现
- C++引用(1) - 基本介绍
- C++文本查询程序 定义类管理数据 用引用共享数据 不用智能指针 C++Primer练习12.27