前面讲过了强指针和弱指针为什么会存在以及循环引用如何解决?但是可能还是比较模糊,为什么会出现循环引用,最终导致二者的指针都不能得到释放呢?
本篇就将前面的内容用到循环引用中来。
C++新特性22_智能指针循环引用问题原理解析
1.循环引用导致的问题原理
先看一段之前自己写的只能指针的程序:
// TestC11.cpp : 定义控制台应用程序的入口点。
//
#include <iostream>
#include <cstring>
using namespace std;
//智能指针:
// 1. 用起来像指针
// 2. 会自己对资源进行释放
class CStudent
{
public:
CStudent() {}
void test() {
cout << "CStudent" << endl;
m_nSex = 1;
}
private:
char* m_pszBuf;
int m_nSex;
};
template<typename T>
class CSmartPtr;
template<typename T>
class CRefCount
{
friend class CSmartPtr<T>;
public:
CRefCount(T* pStu) {
m_pObj = pStu;
m_nCount = 1;
}
~CRefCount() {
delete m_pObj;
m_pObj = NULL;
}
void AddRef() {
m_nCount++;
}
void Release() {
if (--m_nCount == 0) {
//这么写就表示自己一定要是一个堆对象
delete this;
}
}
private:
T* m_pObj;
int m_nCount;
};
template<typename T>
class CSmartPtr
{
public:
CSmartPtr()
{
m_pRef = NULL;
}
CSmartPtr(T* pStu)
{
m_pRef = new CRefCount<T>(pStu);
}
~CSmartPtr()
{
if (m_pRef != NULL) {
m_pRef->Release();
}
}
CSmartPtr(CSmartPtr& obj)
{
m_pRef = obj.m_pRef;
m_pRef->AddRef();
}
CSmartPtr& operator=(CSmartPtr& obj)
{
if (m_pRef == obj.m_pRef) {
return *this;
}
if (m_pRef != NULL)
{
m_pRef->Release();
}
m_pRef = obj.m_pRef;
m_pRef->AddRef();
return *this;
}
void test2()
{
cout << "test2" << endl;
}
T* operator->()
{
return m_pRef->m_pObj;
}
T** operator&()
{
return &m_pRef->m_pObj;
}
T& operator*()
{
return *m_pRef->m_pObj;
}
operator T*()
{
return m_pRef->m_pObj;
}
private:
CRefCount<T>* m_pRef;
};
class B;
class A
{
public:
A() {}
CSmartPtr<B> m_b; //A类中包含B的智能指针
};
class B
{
public:
B() {}
CSmartPtr<A> m_a; //B类中包含A的智能指针
};
int main(int argc, char* argv[])
{
{
CSmartPtr<A> a = new A;
CSmartPtr<B> b = new B; //b引用计数+1
a->m_b = b; //b赋值给a中的m_b,b引用计数+1=2
b->m_a = a;
}
return 0;
}
在未出作用域之前,a,b已经被引用两次
出了作用域之后,引用计数仍然是1,说明资源未被成功释放,这与之前的share_ptr是类似效果。
那么如果没有循环引用,资源是否可以得到有效释放呢?
int main(int argc, char* argv[])
{
{
CSmartPtr<A> a = new A;
CSmartPtr<B> b = new B;
//a->m_b = b;
//b->m_a = a;
}
return 0;
}
没有循环引用情况下,引用计数为1
出块作用域之后,资源得到释放,引用计数为0
2.循环引用导致无法正常释放的原因
- 互相仅仅将引用计数从2减为1,都在等待对方将自己的引用结束掉,使得引用计数为0,才能使用
Release()
中的delete this
结束自己 - 两个人都持有对方的指针,都不愿释放,最终都不释放,类似于死锁状态
(两个人打架,互相撕扯着对方的头发,一个不撒手,另一个绝对不会撒手)
3. 怎么解决循环引用不能释放的问题呢?
让其中一个的引用计数,设为1,这样在析构时计数为1的就会先释放,这样另一个循环引用的也会变为1(一方撒手,另一方也就撒手)
这就是weak_ptr的作用!
4.学习视频地址:智能指针循环引用问题原理解析