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. 转载:ubuntu 下的dpkg 的用法

    dpkg是一个Debian的一个命令行工具,它可以用来安装.删除.构建和管理Debian的软件包. 下面是它的一些命令解释: 1)安装软件 命令行:dpkg -i <.deb file name ...

  2. CI框架下 新浪微博登录接口完整版

    https://www.cnblogs.com/yznyzcw/p/3756622.html#top 说明:本贴只适合CI框架.功能实现:登录接口跳转链接成功,获取用户信息(包括最重要的u_id)成功 ...

  3. visual studio 2013 修改mvc5的视图模板

    C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffol ...

  4. Tensorflow中tf.ConfigProto()详解

    参考Tensorflow Machine Leanrning Cookbook tf.ConfigProto()主要的作用是配置tf.Session的运算方式,比如gpu运算或者cpu运算 具体代码如 ...

  5. iptablesIP规则的保存与恢复

    iptables-save把规则保存到文件中,再由目录rc.d下的脚本(/etc/rc.d/init.d/iptables)自动装载 使用命令iptables-save来保存规则.一般用 iptabl ...

  6. jQuery签名插件jSignature

    1.引入jSignature.min.js和jquery.min.js文件2.代码 <div id="signature"></div> 3.js 初始化 ...

  7. Python工程编译成跨平台可执行文件(.pyc)

    原文:https://blog.csdn.net/zylove2010/article/details/79593655 在某些场景下,若不方便将python编写的源码工程直接给到其他人员,则可以将p ...

  8. python模块之序列化模块

    序列化 """ 序列--字符串 序列化--其他数据类型转化为字符串数据类型 反序列化--字符串转化为其他数据类型 """ json模块 &q ...

  9. P1022 绵羊排序

    题目描述 聪聪想要给他家农场里的 \(n\) 只绵羊按照品质从高到低进行排序. 农场里的 \(n\) 只绵羊编号从 \(1\) 到 \(n\) ,第 \(i\) 只绵羊的体重为 \(w_i\) ,高度 ...

  10. 2019-6-5-WPF-隐藏系统窗口菜单

    title author date CreateTime categories WPF 隐藏系统窗口菜单 lindexi 2019-06-05 17:26:44 +0800 2019-06-05 17 ...