1:引用计数:
- 引用计数是为了防止内存泄漏而产生的一种方法,其基本思想是对于动态分配的对象,进行引用计数,每当增加一次对对象的引用,那么引用对象的引用计数就正价一次,每删除一次引用,引用计数就会减一次,当一个引用对象的引用计数为零时,就会自动删除指向的堆内存。
- 在传统C++编程中,需要手动释放资源来避免忘记释放资源带来的资源泄漏,这是通过 new和delete实现的,C++11中通过引入智能指针的概念,使用了引入计数的想法,使得程序员不需要关心手动释放内存,这些智能指针包括 : std::shared_prt,std::unique_ptr,std::weak_ptr 。使用他们需要包含头文件。
unique_ptr , shared_ptr,weak_ptr 。
2:std::shared_ptr
2.1 原理简介
std::shared_ptr是C++ 提供的最常用的智能指针,采用引用计数,多个shared_ptr中的 T *ptr指向同一个内存区域(同一个对象),并且他们(多个shared_ptr)共同维护一个引用计数器,记录着同一个实例被引用的次数,当引用次数大于0时内存可用,等于0时释放内存。实现了任何地方都不是使用时自动删除指针,帮助解决消除内存泄漏和悬空指针的问题。
2.2 :底层原理解析
每个std::shared_ptr指针在内部维护着两个内存位置:
- 指向对象的指针
- 用于控制引用计数数据的指针
- 当新的 shared_ptr对象与指针关联时,则在其构造函数中,将与此指针关联的引用计数增加1。
- 当任何 shared_ptr对象超出作用域时,则在析构函数中,将与此指针关联的引用计数减1。
- 如果最终引用计数为0,就表示没有其他任何 shared_ptr对象与此内存关联,那么在这种情况下,shared_ptr就会自动调用 delete函数删除内存,不需要程序员自己处理,完美的解决了内存泄漏的隐患
- 注意:shared_ptr最大的一个陷阱就是循环引用的问题,循环引用会倒追内存无法正常正确的被释放,从而导致内存泄漏。
下面是 shared_ptr 内部原理实例。
// shared_ptr 伪代码
template<typename T>
class Shared_ptr {
pubolic:
........
private:
T *_ptr; // 指针指向的内存区域
int *_refCount; // 引用次数
}
3:std::weak_ptr
案例:shared_ptr 带来的循环引用问题
#include<iostream>
#include<vector>
using namespace std;
class ClassB;
class ClassA
{
public:
ClassA()
{
cout << "ClassA Constructor...." << endl;
}
~ClassA()
{
cout << "ClassA Destructor....." << endl;
}
// 在 A中引用 B
shared_ptr<ClassB> pb;
};
class ClassB
{
public:
ClassB() {
cout << "ClassB Constructor....." << endl;
}
~ClassB()
{
cout << "ClassB Destructor....." << endl;
}
// 在B中引用A
shared_ptr<ClassA> pa;
};
int main() {
shared_ptr<ClassA> spa = make_shared<ClassA>();
shared_ptr<ClassB> spb = make_shared<ClassB>();
spa->pb = spb;
spb->pa = spa;
}
在上面代码中,ClassA和ClassB之间存在着循环引用,从运行结果可以看到:当main函数运行结束后,spa和spb管理的动态资源并没有得到释放,产生了内存泄漏。
所以为了解决类似这样的问题,C++11引入了 weak_ptr ,来打破这种循环引用。