总结下个人对智能指针的理解,手写一个简易的c++实现,最后整理一下相关知识点,有不准确的部分欢迎大佬指正。
动态资源的管理一直是一个头疼的问题,因为动态内存的特殊性,其并不会在程序的运行过程中自动进行释放,那么在动态内存上构造的对象也就不会进行析构,所以早期的动态对象的管理需要开发人员自己去确定该动态对象最后的使用时间,并在确定没有变量对其操作后主动调用delete或delete[]进行释放。而这个确定释放时间的过程是非常容易出错的,可能是开发人员无意识的疏忽,也可能是异常导致的流程跳转,最后导致内存泄露或者重复释放。内存泄露会使该部分内存资源不可用,以及同样重要的,动态对象所持有的资源无法释放。而重复释放则可能会导致程序crash。
于是智能指针应运而生,承担了删除动态对象释放内存的责任。智能指针利用c++ RAII的特性和模板化编程,本质上是一个包装类,使用起来像普通指针一样方便。当最后一个引用动态对象的智能指针离开作用域或不在引用动态对象后对其进行清理。
就像上面提到的,无论是手动管理还是智能指针,都需要在确定最后一个引用该动态对象的指针释放后清理。于是顺势就引出了所有权问题,当一个动态对象只会被一个指针引用时为独占所有权,被多个指针引用时为共享所有权。独占所有权的指针在释放时直接删除对象,共享所有权的指针则在最后一个指针释放时删除对象。其实可以看出来,独占指针就是一种特殊的共享指针,之所以在使用时进行区分也是考虑到各自的代码复杂程度,独占指针的实现要更简单,资源占用更少。
这里为了图省事只实现了构造函数、析构函数和基本的运算符,仅供参考。
template <typename T>
class unique_ptr {
public:
unique_ptr() = default;
unique_ptr(std::nullptr_t) {}
explicit unique_ptr(T* t) { // 单参构造函数通过explicit禁止隐式转换
_p = t;
}
unique_ptr(const unique_ptr<T>&) = delete;
unique_ptr(unique_ptr<T>&& t) {
this->_p = t._p;
t._p = nullptr;
}
~unique_ptr() {
if(_p) {
delete _p;
}
}
T* operator->() {
return _p;
}
T& operator*() {
return *_p;
}
unique_ptr<T>& operator=(unique_ptr<T>&& t) {
if(_p) {
delete _p;
}
_p = t._p;
t._p = nullptr;
return *this;
}
unique_ptr<T>& operator=(std::nullptr_t) {
if(_p) {
delete _p;
}
_p = nullptr;
return *this;
}
private:
T* _p{nullptr};
};
template <typename T>
class unique_ptr<T[]> {
public:
unique_ptr() = default;
unique_ptr(std::nullptr_t) {}
explicit unique_ptr(T* t) {
_p = t;
}
unique_ptr(const unique_ptr<T[]>&) = delete;
unique_ptr(unique_ptr<T[]>&& t) {
this->_p = t._p;
t._p = nullptr;
}
~unique_ptr() {
if(_p) {
delete[] _p;
}
}
T& operator[](std::size_t i) {
return _p[i];
}
unique_ptr<T[]>& operator=(unique_ptr<T[]>&& t) {
if(_p) {
delete[] _p;
}
_p = t._p;
t._p = nullptr;
return *this;
}
unique_ptr<T[]>& operator=(std::nullptr_t) {
if(_p) {
delete[] _p;
}
_p = nullptr;
return *this;
}
private:
T* _p{nullptr};
};
class shared_count{
public:
shared_count() = default;
std::mutex m;
size_t ref_count{1};
};
template <typename T>
class shared_ptr{
public:
shared_ptr() = default;
shared_ptr(std::nullptr_t) {};
explicit shared_ptr(T* t) {
_shr_cnt = new shared_count();
_p = t;
};
shared_ptr(const shared_ptr<T>& t) {
if(t._shr_cnt) {
std::unique_lock<std::mutex> lock(t._shr_cnt->m);
t._shr_cnt->ref_count += 1;
this->_shr_cnt = t._shr_cnt;
this->_p = t._p;
}
}
shared_ptr(shared_ptr<T>&& t) {
if(t._shr_cnt) {
this->_shr_cnt = t._shr_cnt;
this->_p = t._p;
t._shr_cnt = nullptr;
t._p = nullptr;
}
}
shared_ptr<T>& operator=(const shared_ptr<T>& t) {
if(this == &t) {
return *this;
}
_release();
if(t._shr_cnt) {
std::unique_lock<std::mutex> lock(t._shr_cnt->m);
t._shr_cnt->ref_count += 1;
this->_shr_cnt = t._shr_cnt;
this->_p = t._p;
}
return *this;
}
shared_ptr<T>& operator=(shared_ptr<T>&& t) {
if(this == &t) {
return *this;
}
_release();
if(t._shr_cnt) {
this->_shr_cnt = t._shr_cnt;
this->_p = t._p;
t._shr_cnt = nullptr;
t._p = nullptr;
}
}
T* operator->() {
return _p;
}
T& operator*() {
return *_p;
}
~shared_ptr() {
_release();
}
private:
T* _p{nullptr};
shared_count* _shr_cnt{nullptr};
void _release() {
if(_shr_cnt) {
std::unique_lock<std::mutex> lock(_shr_cnt->m);
_shr_cnt->ref_count -= 1;
if(_shr_cnt->ref_count == 0) {
delete _shr_cnt;
delete _p;
}
_shr_cnt = nullptr;
_p = nullptr;
}
}
};
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。