当前位置:网站首页>智能指针的说明与简单实现
智能指针的说明与简单实现
2022-07-15 10:30:00 【芜湖开冲~】
本博客代码在vs2022底下调试与实现
目录
为什么需要智能指针?
因为我们很容易对于指针管理的资源忘记释放,这就很容易造成资源泄漏
RAII
提到智能指针,就不得不提一下RAII,这是一种思想
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
不需要显式地释放资源。
采用这种方式,对象所需的资源在其生命期内始终保持有效。
说白了,就是把资源进行包装,利用析构函数的特性,来帮助我们释放资源
auto_ptr
auto_ptr的实现
#pragma once
namespace czz {
template <class T>
class auto_ptr {
public:
auto_ptr(T* ptr = nullptr)
:_ptr(ptr)
, _isOwner(false)
{
if (_ptr != nullptr) {
_isOwner = true;
}
}
~auto_ptr() {
if (_ptr != nullptr && _isOwner == true) {
delete _ptr;
_ptr = nullptr;
}
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
//解决资源释放的问题:资源转移
auto_ptr(const auto_ptr<T>& ap)
:_ptr(ap._ptr)
, _isOwner(ap._isOwner) {
ap._isOwner = false;
}
auto_ptr<T>& operator=(const auto_ptr<T>& ap) {
if (this != &ap) {
if (_ptr != nullptr && _isOwner == true) {
delete _ptr;
}
_ptr = ap._ptr;
_isOwner = ap._isOwner;
ap._isOwner = false;
}
return *this;
}
private:
T* _ptr;
//_isOwner有两个用处,判断资源是否存在,以及此对象是否拥有释放权;
mutable bool _isOwner;
};
}对auto_ptr的研究
先谈谈历史,auto_ptr是c++98里面提出的概念,在后来的修正中改过一次,但是最后又改回去了.后来标准库给的建议是:任何情况下都不建议使用.
那既然改来改去,就表示有问题嘛,而且还没有得到解决,所以才建议任何情况下都不建议使用.
98版auto_ptr(上面代码)的问题
我们知道,一个类,要有拷贝构造和赋值运算符重载,那对于包装的指针,浅拷贝肯定不行,因为它涉及到了资源的管理,如果两个对象用一个资源,那一个释放资源以后,另一个怎么办?所以98版解决这个问题的办法就是资源转移,就是拷贝构造或者赋值运算符重载的时候,把前一个对象的资源交给后一个对象,但这也有一个问题:两个指针不能同时指向同一个资源.
后来修正的auto_ptr的问题
修正,自然就是解决前面的问题,那是怎么修正的呢,就是按照我上面的代码,也就是大家都能使用,但只有一个有资格释放,但是这下问题更大了,可能直接就造成野指针了,比如前面已经把资源释放了,后面又用指向这个资源的其他的指针,所以c++11又改回去了.那c++11到底搞了什么,让人们放弃使用auto_ptr呢?往下看.
unique_ptr
unique_ptr实现
简单的让大家看个思想
#pragma once
namespace czz {
template <class T>
class unique_ptr {
public:
unique_ptr(T* ptr)
:_ptr(ptr)
{}
~unique_ptr() {
if (_ptr != nullptr) {
delete _ptr;
_ptr = nullptr;
}
}
unique_ptr(const unique_ptr<T>& up) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
private:
T* _ptr;
};
}相信大家也能看懂,就是直接不允许实现拷贝构造和赋值运算符重载,这个没什么好说的,好用,唯一可惜的就是一份资源只能由一个指针管理
shared_ptr
这个是为了解决上面的那个问题而实现的,对于资源问题,也就是涉及到拷贝构造和赋值运算符重载,它采用的就是引用计数,先看代码
shared_ptr的实现
#pragma once
#include <mutex>
namespace czz {
template<class T>
struct DFDef {
void operator()(T*& ptr) {
if (ptr) {
delete ptr;
ptr = nullptr;
}
}
};
template <class T, class DF = DFDef<T>> //可能资源不是new的,所以需要别的方法去释放
class shared_ptr {
public:
shared_ptr(T* ptr = nullptr)
:_ptr(ptr), _pRefCount(nullptr), _pMutex(nullptr) {
if (_ptr) {
_pRefCount = new int(1);
_pMutex = new std::mutex;
}
}
~shared_ptr() {
Release();
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
shared_ptr(const shared_ptr<T, DF>& sp)
:_ptr(sp._ptr), _pRefCount(sp._pRefCount) {
_pMutex = new std::mutex;
AddRef();
}
shared_ptr<T, DF>& operator=(const shared_ptr<T, DF>& sp) {
if (_ptr != sp._ptr) {
Release();
_ptr = sp._ptr;
_pRefCount = sp._pRefCount;
_pMutex = new std::mutex;
AddRef();
}
return *this;
}
size_t use_count()const {
return *_pRefCount;
}
T* Get() {
return _ptr;
}
private:
void AddRef() {
if (_pRefCount != nullptr) {
_pMutex->lock();
++(*_pRefCount);
_pMutex->unlock();
}
}
int SubRef() {
_pMutex->lock();
--(*_pRefCount);
_pMutex->unlock();
return *_pRefCount;
}
void Release() {
if (_ptr != nullptr && SubRef() == 0) {
DF()(_ptr);
delete _pRefCount;
delete _pMutex;
_ptr = nullptr;
_pRefCount = nullptr;
}
}
private:
T* _ptr;
int* _pRefCount;
std::mutex* _pMutex;
};
}shared_ptr的问题
我们举个栗子:
对于一个双向链表的一个节点,我们可以把里面它本身和它里面的两个指向前(prev)后(next)的指针都改成shared_ptr,现在有一个链表有两个节点,A节点的next指向B节点,B节点的prev指向A节点,那现在不要B节点了,它的引用计数减1,但因为A节点的next还指向这B节点的东西,所以B节点的资源不能直接释放,同理,A节点也一样,而且推导下去,你会发现,这种情况下,无论如何都无法释放这两个资源
解决问题的办法
weak_ptr:这个就是专门解决shared_ptr的,也很简单,就是只有主资源采用shared_ptr,其他的采用weak_ptr就行了,在上面的例子里面,对于节点本身,我们用shared_ptr管理,节点里面的如prev和next,我们采取weak_ptr就可以了,至于原理,大概说一下,就是引用计数其实不是一个,而是两个,一个管理shared_ptr,一个管理weak_ptr
边栏推荐
- Codeforces Round #806 (Div. 4)
- Software testing? Monthly salary 20K +? I can't automate the test. It's really hard for me
- Pytorch(二)——PyTorch的主要组成模块
- Dbpack read / write separation function release announcement
- Solution of in multi column special query type in laravel
- Common vulnerability types of tcp/ip protocol
- 001层次选择器
- Codeforces Round #806 (Div. 4)
- 有证书有目录!|海泰密码服务平台
- It's time to say goodbye gracefully to nullpointexception
猜你喜欢

kali安装Nessus

Matlab底层源码实现图像动态二值化

Sidecar traffic management and multi protocol sniffing in service grid

服务网格中 sidecar 流量治理与多协议嗅探

2 小时写了篇论文,凭什么 GPT-3 不配拥有姓名?

Matlab底层源代码实现图像腐蚀,膨胀操作(与Halcon效果一致)

030.文件的删除和链接

To further improve the digital and intelligent level, Asia lion's first product is refreshed, with a price of 141500

Talking about some features of improving work efficiency supported by slack channel

Restore of data encryption returned by a website
随机推荐
A Tong's little wish
Kali install Nessus
How Axure achieves screen adaptation
云原生:Docker实践经验(三)Docker 上部署 MySQL8 主从复制
Network security (2)
UML class diagram
030.文件的删除和链接
这可是全网网工基础知识最详细的整理,没有之一
数据湖(十七):Flink与Iceberg整合DataStream API操作
基於機器學習的笑臉檢測
不同类型的印刷机作用
Analysis of technical design details dynamically displayed or hidden by SAP Fiori application adapt UI
Guys, the capture delay of flick CDC Oracle is particularly high. Is there any optimization method
Data Lake (19): SQL API reads Kafka data and writes it to iceberg table in real time
Talking about some features of improving work efficiency supported by slack channel
Hi guys, I would like to ask why startupoptions in MySQL CDC only supports initi
Halcon常用图像预处理算子总结
学习信也杯-train代码学习
Common vulnerability types of tcp/ip protocol
Typescript 14 starting from 0: built in tool type