C++引用计数设计与分析(解决垃圾回收问题)
1.引言
上一篇博文关于浅拷贝和深拷贝 https://www.cnblogs.com/zhaoyixiang/p/12116203.html
我们了解到我们在浅拷贝时对带指针的对象进行拷贝会出现内存泄漏,那C++是否可以实现像python,JAVA一样引入
垃圾回收机制,来灵活的来管理内存。
遗憾的是C++并不像python、java等编程语言一样有着垃圾回收机制(Gabage Collector),因此导致了C++中对动态
存储的管理称为程序员的噩梦,出现了内存遗失(memory leak)、悬空指针、非法指针存取等问题。
Bjarne本人认为:
“我有意这样设计C++,使它不依赖于自动垃圾回收(通常就直接说垃圾回收)。这是基于自己对垃圾回收系统的经验,
我很害怕那种严重的空间和时间开销,也害怕由于实现和移植垃圾回收系统而带来的复杂性。还有,垃圾回收将使C+
+不适合做许多底层的工作,而这却正是它的一个设计目标。但我喜欢垃圾回收的思想,它是一种机制,能够简化设计、
排除掉许多产生错误的根源。
C++中提供的构造函数和析构函数就是为了解决了自动释放资源的需求。Bjarne有一句名言,“资源需求就是初始化(Resource Inquirment Is Initialization)”。
因此,我们可以将需要分配的资源在构造函数中申请完成,而在析构函数中释放已经分配的资源。
在C++中,允许你控制对象的创建,清楚和复制,我们就可以通过开发一种称为引用计数的垃圾回收机制实现这种控制
2.设计思想
首先我们明确在对存在指针的对象构造时,析构对象时需要把指针delete(释放掉),但是此时如果我们对对象进行浅拷贝,没有新的指针new。
析构对象时候会出现内存泄漏(一个指针所指的内存被两次释放的清况),我们用通过引用计数来解决这个问题:
每构造一个对象,就创建一个新的计数器并+1.每拷贝构造一次就在被拷贝的那个对象所在的计数器上+1;
析构时候 按照构造函数析构的顺序(后造先放,类似栈),最后构造或拷贝的先释放;
每次释放先对计数器-1并判断计数器是否为0(是否存在浅拷贝的对象),大于0时,继续按照析构顺序析构下一个对象;
当计数器为0时,释放指针。
3.举例
我们按顺序构造3个对象,计数器标号记为 1,2,3,对第一个和第三个对象浅拷贝两次,
对对象拷贝完成后计数器1,2,3的值分别为 2 1 2.
先释放计数器3 计数器-1后等于1,析构掉一个对象。计数器为 2 1 1
再释放计数器1 计数器-1后等于1,析构掉一个对象。计数器为 1 1 1
再释放计数器3 计数器-1后等于0,析构掉一个对象,并释放掉指针。计数器为 1 1 空
再释放计数器2 计数器-1后等于0,析构掉一个对象,并释放掉指针。计数器为 1 空 空
再释放计数器1 计数器-1后等于0,析构掉一个对象,并释放掉指针。计数器为 空 空 空
最终所有对象析构完毕,指针也全部释放完
4.代码
//引用计数类 class CRefCount
{
public:
CRefCount(); //构造计数器对象
CRefCount(const CRefCount& obj); //拷贝构造计数器
void* Alloc(int size); //构造对象时申请空间
int AddRef(); //计数增加
int ReleaseRef(); //计数减少
~CRefCount(); private:
void* m_pBuf; //指针缓冲区
int* m_pRefCount; //计数
}; CRefCount::CRefCount()
{
m_pBuf = nullptr;
m_pRefCount = nullptr;
} CRefCount::CRefCount(const CRefCount& obj)
{
m_pBuf = obj.m_pBuf;
m_pRefCount = obj.m_pRefCount;
AddRef();
} void* CRefCount::Alloc(int size)
{
m_pBuf = new char[size + 1]; //申请缓冲区
m_pRefCount = new int(0);
AddRef(); //每次构造对象计数+1 return m_pBuf;
} int CRefCount::AddRef()
{
if (m_pRefCount == nullptr)
return 0;
return ++(*m_pRefCount);
} int CRefCount::ReleaseRef()
{
if (m_pRefCount == nullptr)
return 0; return --(*m_pRefCount);
} CRefCount::~CRefCount()
{
if (ReleaseRef() == 0)
{
if (m_pBuf != nullptr)
{
delete[] m_pBuf;
m_pBuf = nullptr;
delete m_pRefCount;
m_pRefCount = nullptr;
}
}
}
5.测试
//student测试用例
#include"CRefCount.h"
#include<iostream>
#pragma warning(disable:4996) using namespace std; class CStudent
{
private:
char* m_pName;
CRefCount m_RefCount;
const char* GetName() const;
public:
CStudent(const char* pName);
}; const char* CStudent::GetName() const
{
return m_pName;
} CStudent::CStudent(const char* pName)
{
m_pName = (char*)m_RefCount.Alloc(strlen(pName) + 1); //申请一个用来存放名字的空间
strcpy(m_pName, pName);
} int main()
{
CStudent s1("shadow");
CStudent s2("iceice");
CStudent s3("maybe");
CStudent s4 = s1;
CStudent s5 = s3; return 0;
}
调试这个程序,我们在完成构造和拷贝后,查看内存,可以看到此时计数器1,2,3分别对应的值为2,1,2

单步跟入,看到第一个拷贝构造的对象被析构掉,计数器值-1 ,此时3个计数器值分别为为2,1,1

再继续往后走,发现第二个拷贝对象析构掉切指针所指的内存还未被释放掉,计数器1 -1,此时计数器值为 1,1,1

再向后执行,此时第三个构造的对象开始被析构掉同时计数器减到0,此时对象3的指针被释放掉。

加上辅助调试代码,最终可以看到执行结果,构造3次,拷贝2次,释放3次,完成了引用计数功能

C++引用计数设计与分析(解决垃圾回收问题)的更多相关文章
- JVM中垃圾回收机制如何判断是否死亡?详解引用计数法和可达性分析 !
因为热爱,所以坚持. 文章下方有本文参考电子书和视频的下载地址哦~ 这节我们主要讲垃圾收集的一些基本概念,先了解垃圾收集是什么.然后触发条件是什么.最后虚拟机如何判断对象是否死亡. 一.前言 我们 ...
- JVM 基础:回收哪些内存/对象 引用计数算法 可达性分析算法 finalize()方法 HotSpot实现分析
转自:https://blog.csdn.net/tjiyu/article/details/53982412 1-1.为什么需要了解垃圾回收 目前内存的动态分配与内存回收技术已经相当成熟,但为什么还 ...
- java中垃圾回收机制中的引用计数法和可达性分析法(最详细)
首先,我这是抄写过来的,写得真的很好很好,是我看过关于GC方面讲解最清楚明白的一篇.原文地址是:https://www.zhihu.com/question/21539353
- 强引用,弱引用,4种Java引用浅解(涉及jvm垃圾回收)
http://www.jb51.net/article/49085.htm http://www.jb51.net/article/49085.htm
- C++ 引用计数技术及智能指针的简单实现
一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...
- 【JVM】JVM系列之垃圾回收(二)
一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收.除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此.所以,垃圾回收是必须的. 二. ...
- Python的 垃圾回收机制
垃圾回收 1. 小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间. Python 对小整数的定义是 [-5, 257) 这些整 ...
- python之MRO和垃圾回收机制
一.MOR 1.C3算法简介 为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题. python2.3版本之后不管是新式类还是经典类,查找继承顺序都采用C3算法 2.算法原理 C3算法的 ...
- day05基本运算符,格式化输出,垃圾回收机制
内容大纲:1.垃圾回收机制详解(了解) 引用计数 标记清除 分代回收 2.与用户交互 接收用户输入 # python3中 input # python2.7(了解) input raw_input 格 ...
随机推荐
- 2017 ACM/ICPC Asia Regional Shenyang Online:number number number hdu 6198【矩阵快速幂】
Problem Description We define a sequence F: ⋅ F0=0,F1=1;⋅ Fn=Fn−1+Fn−2 (n≥2). Give you an integer k, ...
- ntelliJ IDEA2017 + tomcat 即改即生效 实现热部署
1.点击idea中tomcat设置 2.点击deployment查看Deploy at the server startup 中tomcat每次所运行的包是 xxxx:war 还是其他,如果是xxxx ...
- LeetCode Weekly Contest 6
leetcode现在每周末举办比赛,这是今天上午参加的比赛的题解.题目难度不算大,两个easy,一个medium,一个hard.hard题之前接触过,所以做得比较顺利. 1. Sum of Left ...
- oracle函数 NLS_INITCAP(x[,y])
[功能]返回字符串并将字符串的第一个字母变为大写,其它字母小写; [参数]x字符型表达式 [参数]Nls_param可选, 查询数据级的NLS设置:select * from nls_database ...
- 利用阿里云容器服务打通TensorFlow持续训练链路
本系列将利用Docker和阿里云容器服务,帮助您上手TensorFlow的机器学习方案 第一篇:打造TensorFlow的实验环境 第二篇:轻松搭建TensorFlow Serving集群 第三篇:打 ...
- js获取本周日期
JS获取到本周的日期 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> &l ...
- @noi.ac - 170@ 数数
目录 @description@ @solution@ @accepted code@ @details@ @description@ 求有多少对 1 ∼ n 的排列 (a, b) 满足 \(m \l ...
- 20190528-JavaScriptの打怪升级旅行 { 语句 [ 赋值 ,数据 ] }
写在前面的乱七八糟:今天考了试,emmm很基础的题,还是Mrs房的面试题让人绝望啊┓( ´∀` )┏,补了很多知识,很综合的题,坑也很多,总的来说,查漏补缺,其实是啥都缺~ 今天打的小BOSS主要是数 ...
- Hibernate懒加载导致json数据对象传输异常的问题---(非常重要)
1. 异常: [console_demo][WARN] [2016-12-15 19:49:35] org.springframework.web.servlet.mvc.support.Defaul ...
- 谈谈数据库的 ACID(转)
一.事务 定义:所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位. 准备工作:为了说明事务的ACID原理,我们使用银行账户及资金管理的案例进行分析. 二.ACI ...