一、 原理 :RAII 资源获取即初始化技术

原理:接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈上的变量自动被销毁,智能指针内部保存的内存也就被释放掉了(除非将智能指针保存起来)。

为什么这么6呢?

在传统 C++ 中,『记得』手动释放资源,总不是最佳实践。因为我们很有可能就忘记了去释放资源而导致泄露。所以通常的做法是对于一个对象而言,我们在构造函数的时候申请空间,而在析构函数(在离开作用域时调用)的时候释放空间,也就是我们常说的 RAII 资源获取即初始化技术。

#include <iostream>
#include <memory>
using namespace std;

class CData
{
public:
CData(int iSize) :m_iSize(iSize)
{
}
public:
int m_iSize;
static CData* m_CDataModule;
};

CData* CData::m_CDataModule = new CData(19);

bool PtrIsNull()
{
if(NULL == CData::m_CDataModule)
{
return false;
}
return true;
}
void TestFunc()
{
bool bResult = PtrIsNull();
cout <<"01 CData::m_CDataModule->m_iSize " << CData::m_CDataModule->m_iSize << endl;
shared_ptr<CData> shPtrA(CData::m_CDataModule);
}
int main()
{
TestFunc();
bool bResult = PtrIsNull();
cout << "02 CData::m_CDataModule->m_iSize " << CData::m_CDataModule->m_iSize << endl;
int iCount = 0;

cin.get();
return 0;
}

这是在使用智能指针之前

01 CData::m_CDataModule->m_iSize 19
02 CData::m_CDataModule->m_iSize 19

使用智能指针之后,程序第二次输入杂乱无章的数值,是因为堆空间被释放掉了

01 CData::m_CDataModule->m_iSize 19
02 CData::m_CDataModule->m_iSize -572662307

注意PtrIsNull()返回的是 true,这是因为智能指针对堆空间进行了释放,但是对原来的指针没有进行操作

二、引用计数

引用计数是这样一个技巧,记录同一个实例被引用的次数,当引用次数大于0时可用,等于0时释放内存。引用计数的使用常有两个目的:

  • 简化跟踪堆中(也即C++中new出来的)的对象的过程。一旦一个对象通过调用new被分配出来,记录谁拥有这个对象是很重要的,因为其所有者要负责对它进行delete。但是对象所有者可以有多个,且所有权能够被传递,这就使得内存跟踪变得困难。引用计数可以跟踪对象所有权,并能够自动销毁对象。可以说引用计数是个简单的垃圾回收体系。这也是本文的讨论重点。
  • 节省内存,提高程序运行效率。如何很多对象有相同的值,为这多个相同的值存储多个副本是很浪费空间的,所以最好做法是让左右对象都共享同一个值的实现。C++标准库中string类采取一种称为”写时复制“的技术,使得只有当字符串被修改的时候才创建各自的拷贝,否则可能(标准库允许使用但没强制要求)采用引用计数技术来管理共享对象的多个对象。
#include <iostream>
#include <memory>
using namespace std;

class CData
{
public:
CData(int iSize) :m_iSize(iSize)
{
}
public:
int m_iSize;
static CData* m_CDataModule;
};

CData* CData::m_CDataModule = new CData(19);

int main()
{
shared_ptr<CData> shaPtrCDataA(CData::m_CDataModule);
shared_ptr<CData> shaPtrCDataB = shaPtrCDataA;

cout << "Now Use Count"<< shaPtrCDataB.use_count() << endl;

shared_ptr<CData> shaPtrCDataC= shaPtrCDataB;
cout << "Now Use Count" << shaPtrCDataB.use_count() << endl;

shaPtrCDataA.reset();
cout << "Now Use Count" << shaPtrCDataB.use_count() << endl;
cin.get();
return 0;
}

输出如下:

Now Use Count2
Now Use Count3
Now Use Count2
在这里reset执行一次,引用次数就少一次