1. 概述
本篇对共享所有权的智能指针进行仿真。
仿真指针主要有:boost/c++ 11 tr1中的shared_ptr、weak_ptr。
boost与c++ 11 tr1的关系这里稍微做一下类比,shared_ptr在boost中时可以认为是储君(准标准),进入到了tr1之后就正名了就算正式登基了是完全标准的了。
2. this指针的探讨
this指针是编译器默认生成到成员函数中的,类型为T* const this。
例如写一个简单的成员函数。
class A
{
public:
void func(){}
};
编译器处理起来如下。
void func(A* const this);
接下来要思考的问题就是。
1) this指针可以删除吗?
2) 执行删除操作有引发哪些问题?
2.1. delete this源码
成员函数内进行delete this操作源码如下。
class CTest
{
public:
CTest() : m_nVal(0){}
~CTest() {}
/// @brief 释放自身内存
void Release() {delete this;}
private:
int m_nVal;///< 测试成员变量 };
2.2. delete this栈检测
对于栈分配,进行delete检测。
void TestStackDelete()
{
/// @brief 检测栈释放
CTest test;
test.Release();
}
int _tmain(int argc, _TCHAR* argv[])
{
TestStackDelete();
return 0;
}
运行程序会报错。
2.3. delete this堆检测
对于堆分配,进行delete检测。
void TestHeapDelete()
{
/// @brief 检测堆释放
CTest* pTest = new CTest;
pTest->Release();
}
int _tmain(int argc, _TCHAR* argv[])
{
TestHeapDelete();
return 0;
}
运行程序后释放内存正常。
2.4. delete this总结
对delete this做下总结。
1) 对栈上分配的空间则不要调用。
2) 可以采用这种方式来释放堆中分配内存。
3) 调用对应接口后,指针将变为野指针,不可再进行后续操作。
3. shared_ptr仿真
3.1. shared_ptr相关问题
带着如下问题来做仿真实现。
1) shared_ptr实现原理?
2) shared_ptr是否就意味着一统天下?
3) shared_ptr使用中的坑?
3.2. 引用计数类仿真
Shared_ptr中采用引用计数来进行所有权共享,当引用计数为0时进行资源释放。首先进行引用计数类仿真MyRefCount.h。
#pragma once
/// @brief 引用计数类
template<class _Ty>
class CMyRefCount
{
public:
CMyRefCount(_Ty *_Px)
: m_nUses(1)
, m_nWeaks(1)
, _Ptr(_Px)
{}
/// @brief 增加强引用计数
void _Incref() {++m_nUses;}
/// @brief 增加弱引用计数
void _Incwref() {++m_nWeaks;}
/// @brief 减少强引用计数
void _Decref()
{
--m_nUses;
if (0 == m_nUses)
{
_Destroy();
_Decwref();
}
}
/// @brief 减少弱引用计数
void _Decwref()
{
--m_nWeaks;
if (0 == m_nWeaks)
{
_Delete_this();
}
}
/// @brief 获取强引用计数
long _Use_count() const {return (m_nUses);}
/// @brief 指针是否已释放
bool _Expired() const {return (0==m_nUses);}
private:
void _Destroy() {delete _Ptr;}
void _Delete_this() {delete this;}
private:
long m_nUses;///< 强引用次数 long m_nWeaks;///< 弱引用次数 _Ty * _Ptr; };
3.3. 共享指针基类仿真
Weak_ptr和shared_ptr的共用基类进行仿真MyPtrBase.h。
#pragma once
#include "MyRefCount.h"
/// @brief shared_ptr/weak_ptr基类
template<class _Ty>
class CMyPtrBase
{
typedef CMyPtrBase<_Ty> _Myt;
public:
/// @brief 获取强引用计数
long use_count() const
{
if (NULL == _Rep) {return 0;}
return (_Rep->_Use_count());
}
protected:
CMyPtrBase()
: _Ptr(nullptr)
, _Rep(nullptr)
{}
/// @brief 指针是否已释放
bool _Expired() const
{
if (NULL == _Rep) {return true;}
return (_Rep->_Expired());
}
/// @brief 获取原生指针
_Ty *_Get() const {return (_Ptr);}
/// @brief 减少强引用计数
void _Decref()
{
if (nullptr == _Rep) {return;}
_Rep->_Decref();
}
/// @brief 重置资源强引用
void _Reset(const _Myt& _Other)
{
_Reset(_Other._Ptr, _Other._Rep, false);
}
void _Reset(_Ty *_Other_ptr, CMyRefCount<_Ty> *_Other_rep, bool bInit)
{
/// @brief 初次赋值则不做增加操作
if (!bInit)
{
if (_Other_rep != nullptr) {_Other_rep->_Incref();}///< other强引用增加
}
if (_Rep != nullptr) {_Rep->_Decref();}///< this强引用减少
_Rep = _Other_rep;
_Ptr = _Other_ptr;
}
/// @brief 减少弱引用计数
void _Decwref()
{
if (nullptr == _Rep) {return;}
_Rep->_Decwref();
}
/// @brief 重置资源弱引用
void _Resetw(const _Myt& _Other)
{
_Resetw(_Other._Ptr, _Other._Rep);
}
void _Resetw(_Ty *_Other_ptr, CMyRefCount<_Ty> *_Other_rep)
{
if (_Other_rep != nullptr) {_Other_rep->_Incwref();}///< other弱引用增加
if (_Rep != nullptr) {_Rep->_Decwref();}///< this弱引用减少
_Rep = _Other_rep;
_Ptr = _Other_ptr;
}
private:
_Ty *_Ptr; ///< 持有指针
CMyRefCount<_Ty> *_Rep; ///< 引用计数
};
3.4. weak_ptr及shared_ptr仿真源码
对应实现类MySharedPtr.h。
#pragma once
#include "MyPtrBase.h"
template<class _Ty>
class CMyWeakPtr;
template<class _Ty>
class CMySharedPtr;
template<class _Ty>
class CMySharedPtr : public CMyPtrBase<_Ty>
{
public:
typedef CMySharedPtr<_Ty> _Myt;
/// @brief 默认构造函数
CMySharedPtr() {}
/// @brief 析构函数中释放资源
~CMySharedPtr()
{
this->_Decref();
}
/// @brief 显示声明传值构造函数
explicit CMySharedPtr(_Ty *_Px)
{
this->_Reset(_Px, new CMyRefCount<_Ty>(_Px), true);
}
/// @brief 拷贝构造函数
CMySharedPtr(const _Myt& _Other)
{
this->_Reset(_Other);
}
/// @brief 赋值操作符
_Myt& operator=(const _Myt& _Other)
{
this->_Reset(_Other);
return (*this);
}
/// @brief 支持weak_ptr复制
/// 不支持weak_ptr赋值,需要通过lock来转获取share_ptr
explicit CMySharedPtr(const CMyWeakPtr<_Ty>& _Other)
{
this->_Reset(_Other);
}
/// @brief 重置
void reset()
{
this->_Reset(nullptr, nullptr, false);
}
/// @brief 获取原生指针
_Ty *get() const {return (this->_Get());}
/// @brief 仿真指针操作
_Ty *operator->() const { return (this->_Get());}
_Ty &operator*() const { return (*this->_Get());}
/// @brief 是否唯一持有资源
bool unique() const {return (this->use_count() == 1);}
};
template<class _Ty>
class CMyWeakPtr : public CMyPtrBase<_Ty>
{
typedef CMyWeakPtr<_Ty> _Myt;
public:
CMyWeakPtr() {}
~CMyWeakPtr() {this->_Decwref();}
explicit CMyWeakPtr(const _Myt& _Other) {this->_Resetw(_Other);}
CMyWeakPtr& operator=(const _Myt& _Other)
{
this->_Resetw(_Other);
return (*this);
}
/// @brief shared_ptr支持
explicit CMyWeakPtr(const CMySharedPtr<_Ty>& _Other) {this->_Resetw(_Other);}
CMyWeakPtr& operator=(const CMySharedPtr<_Ty>& _Other)
{
this->_Resetw(_Other);
return (*this);
}
/// @brief 重置
void reset() {this->_Resetw(nullptr, nullptr, false);}
/// @brief 资源释放还存在
bool expired() const {return (this->_Expired());}
/// @brief 转化为shared_ptr
CMySharedPtr<_Ty> lock() const
{
return (CMySharedPtr<_Ty>(*this));
}
};
3.5. 仿真指针使用源码
对指针进行使用。
#include "stdafx.h"
#include "MySharedPtr.h"
#include <iostream>
class CTest
{
public:
CTest() {std::cout<<"CTest()"<<std::endl;}
~CTest() {std::cout<<"~CTest()"<<std::endl;}
void test_print() {std::cout<<"test_print()"<<std::endl;}
};
int _tmain(int argc, _TCHAR* argv[])
{
CMySharedPtr<CTest> t1(new CTest);
/// @brief 原生指针检测
t1->test_print();
(*t1).test_print();
std::cout<<t1.use_count()<<std::endl;
{
/// @brief 检测复制
CMySharedPtr<CTest> t2(t1);
std::cout<<t1.use_count()<<std::endl;
}
std::cout<<t1.use_count()<<std::endl;
{
/// @brief 检测赋值
CMySharedPtr<CTest> t3;
t3 = t1;
std::cout<<t1.use_count()<<std::endl;
}
std::cout<<t1.use_count()<<std::endl;
{
/// @brief 检测weak_ptr
CMyWeakPtr<CTest> w1(t1);///< weak_ptr不会增加引用计数
std::cout<<t1.use_count()<<std::endl;
CMySharedPtr<CTest> t4(w1);///< 通过构造函数获取weak_ptr引用指针
std::cout<<t1.use_count()<<std::endl;
CMySharedPtr<CTest> t5;
//t5 = w1;///< 检测weak_ptr直接赋值会编译失败
t5 = w1.lock();///< 通过lock获取weak_ptr引用指针
std::cout<<t1.use_count()<<std::endl;
}
return 0;
}
输出结果:
CTest()
test_print()
test_print()
1
2
1
2
1
1
2
3
~CTest()
3.6. shared_ptr环状引用示例
shared_ptr出现环状引用的话将出现资源泄漏。采用自引用示例来检测。
#include "stdafx.h"
#include "MySharedPtr.h"
#include <iostream>
class CSelfRefTest
{
public:
CSelfRefTest() {std::cout<<"CSelfRefTest()"<<std::endl;}
~CSelfRefTest() {std::cout<<"~CSelfRefTest()"<<std::endl;}
CMySharedPtr<CSelfRefTest> m_pTest;
};
int _tmain(int argc, _TCHAR* argv[])
{
/// @brief shared_ptr产生环形引用时将出现资源泄漏情况
/// 采用自引用示例来说明
CMySharedPtr<CSelfRefTest> p1(new CSelfRefTest);
std::cout<<p1.use_count()<<std::endl;
p1->m_pTest = p1;///< 进行自引用操作
std::cout<<p1.use_count()<<std::endl;
return 0;
}
运行结果:
CSelfRefTest()
1
2
3.7. weak_ptr解决环状引用
weak_ptr引用不会引起计数增加,可用来破环状引用。
#include "MySharedPtr.h"
#include <iostream>
class CSelfRefTest
{
public:
CSelfRefTest() {std::cout<<"CSelfRefTest()"<<std::endl;}
~CSelfRefTest() {std::cout<<"~CSelfRefTest()"<<std::endl;}
CMyWeakPtr<CSelfRefTest> m_pTest;
};
int _tmain(int argc, _TCHAR* argv[])
{
/// @brief shared_ptr产生环形引用时将出现资源泄漏情况
/// 采用weak_ptr来解决该问题
CMySharedPtr<CSelfRefTest> p1(new CSelfRefTest);
std::cout<<p1.use_count()<<std::endl;
p1->m_pTest = p1;///< 进行自引用操作
std::cout<<p1.use_count()<<std::endl;
return 0;
}
运行结果:
CSelfRefTest()
1
1
~CSelfRefTest()
3.8. shared_ptr总结
通过仿真源码,对shared_ptr总结如下。
1) 共享型智能指针通过引用计数方式来实现所有权共享,但同时增加了程序复杂度和运行性能。
2) 避免出现环状引用,这将会引起资源泄漏。
3) weak_ptr是shared_ptr的助手,是弱指针,可用于解除环状引用,但不支持原生指针获取以及->和*操作。