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++引用计数设计与分析(解决垃圾回收问题)的更多相关文章

  1. JVM中垃圾回收机制如何判断是否死亡?详解引用计数法和可达性分析 !

    因为热爱,所以坚持. 文章下方有本文参考电子书和视频的下载地址哦~ 这节我们主要讲垃圾收集的一些基本概念,先了解垃圾收集是什么.然后触发条件是什么.最后虚拟机如何判断对象是否死亡. 一.前言   我们 ...

  2. JVM 基础:回收哪些内存/对象 引用计数算法 可达性分析算法 finalize()方法 HotSpot实现分析

    转自:https://blog.csdn.net/tjiyu/article/details/53982412 1-1.为什么需要了解垃圾回收 目前内存的动态分配与内存回收技术已经相当成熟,但为什么还 ...

  3. java中垃圾回收机制中的引用计数法和可达性分析法(最详细)

    首先,我这是抄写过来的,写得真的很好很好,是我看过关于GC方面讲解最清楚明白的一篇.原文地址是:https://www.zhihu.com/question/21539353

  4. 强引用,弱引用,4种Java引用浅解(涉及jvm垃圾回收)

    http://www.jb51.net/article/49085.htm http://www.jb51.net/article/49085.htm

  5. C++ 引用计数技术及智能指针的简单实现

    一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...

  6. 【JVM】JVM系列之垃圾回收(二)

    一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收.除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此.所以,垃圾回收是必须的. 二. ...

  7. Python的 垃圾回收机制

    垃圾回收 1. 小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间. Python 对小整数的定义是 [-5, 257) 这些整 ...

  8. python之MRO和垃圾回收机制

    一.MOR 1.C3算法简介 为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题. python2.3版本之后不管是新式类还是经典类,查找继承顺序都采用C3算法 2.算法原理 C3算法的 ...

  9. day05基本运算符,格式化输出,垃圾回收机制

    内容大纲:1.垃圾回收机制详解(了解) 引用计数 标记清除 分代回收 2.与用户交互 接收用户输入 # python3中 input # python2.7(了解) input raw_input 格 ...

随机推荐

  1. 破解fireworks_cs6、phoneshop_cs6、dreamweaver_cs6

    我的Adobe密码是绿尘枫加**0,首字母大写,在我的百度云盘有这三款软件的补丁,这三款软件安装和破解的方式都一样.先下载正常安装好正版软件>正常试用一遍之后,fireworks的补丁装错了文件 ...

  2. 远程控制工具&&驱动安装仍然没有声音

    1. 2.下面是一个远程控制工具 TeamViewer

  3. 阿里云POLARDB 2.0重磅来袭!为何用户如此的期待?

    点击报名:POLARDB 2.0 升级发布会 回顾POLARDB 1.0升级之路 POLARDB 1.0主要的改进包括采用了计算存储分离的架构,完全兼容MYSQL,性能是原生MySQL的6倍.一个用户 ...

  4. 深度学习的Xavier初始化方法

    在tensorflow中,有一个初始化函数:tf.contrib.layers.variance_scaling_initializer.Tensorflow 官网的介绍为: variance_sca ...

  5. python selenium 测试配置信息(URL和浏览器)

    config.ini # this is config file, only store browser type and server URL [browserType] #browserName ...

  6. ccf-201703-4-地铁修建

    二分+并查集 发现了一个很奇怪的问题,二分的上界设置错了,结果是运行错误...只有95分.后来改了上界就100了,百思不得其解 #include<cstdio> #include<c ...

  7. 只要是使用函数file_get_contents访问 https 的网站都要开启

    使用file_get_contents();报错failed to open stream: Unable to find the socket transport "ssl" - ...

  8. springboot2.04与activiti 6.0集成

    本文就不对activiti做解释,下面直接看项目集成 以下顺序方面根据我的理解来,可以先从第二章看,再看第一张与第三章 增加activiti表的API,备注用. 目录 一.springboot2.X集 ...

  9. mac常用快捷键,Mac文件重命名快捷键,Mac OS快速访问系统根目录, MacOS 10.11重要数据的存储位置大全

    command+r,相当于F5,刷新页面 command+F5,启动voiceover command+q 关闭当前程序 在Finder中command+/ 打开底部状态栏,可以查看剩余磁盘空间大小 ...

  10. 【CSS3】精美横向滚动菜单按钮

    废话不多说,直接上图: 然后是代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8" ...