通俗易懂学习C++智能指针
时间:2022-07-24
本文章向大家介绍通俗易懂学习C++智能指针,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
智能指针是干什么的?
智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。
什么是内存泄漏?
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而 造成了内存的浪费。
内存泄漏有什么危害?
长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
智能指针的原理
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。
我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效。
智能指针的原理:
- RAII特性(是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术)
- 重载operator*和opertaor->,具有像指针一样的行为。
auto_ptr指针
auto_ptr智能指针的缺点:当对象拷贝或者赋值后,前面的对象就悬空了。
auto_ptr智能指针的模拟实现
template<class T>
class Auto_ptr{
public:
Auto_ptr(T* ptr=nullptr):_ptr(ptr){}
~Auto_ptr(){
if (_ptr)
delete _ptr;
}
Auto_ptr(Auto_ptr<T> &s)//拷贝构造会释放s对象的指针,造成s指针与管理对象断开联系
:_ptr (s._ptr){
s._ptr = NULL;
}
Auto_ptr<T>& operator=(Auto_ptr<T>& s){
if (this != &s._ptr){
if (_ptr)
delete _ptr;
_ptr = s._ptr;
s._ptr = NULL;
}
return *this;
}
T& operator*(){
return *_ptr;
}
T* operator->(){
return _ptr;
}
private:
T* _ptr;
};
unique_ptr指针
unique_ptr的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份UniquePtr来了解它的原理
template<class T>
class Unique_ptr{
public:
Unique_ptr(T* ptr=nullptr):_ptr(ptr){}
~Unique_ptr(){
if (_ptr)
delete _ptr;
}
T& operator*(){
return *_ptr;
}
T* operator->(){
return _ptr;
}
private:
Unique_ptr(Unique_ptr<T> &s)
:_ptr(s._ptr){
s._ptr = NULL;
}
Unique_ptr<T>& operator=(Unique_ptr<T>& s){
if (this != &s._ptr){
if (_ptr)
delete _ptr;
_ptr = s._ptr;
s._ptr = NULL;
}
return *this;
}
private:
T* _ptr;
};
shared_ptr指针
shared_ptr的原理:通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
- shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
- 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
- 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
- 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
模拟实现:
template<class T>
class Shared_ptr{
public:
Shared_ptr(T* ptr)
:_ptr(ptr),_pMutex(new mutex),_pRefrCount(new int(1))
{}
~Shared_ptr(){
Realse();
}
Shared_ptr(const Shared_ptr<T>& sp)
:_ptr(sp._ptr), _pRefrCount(sp._pRefCount), _pMutex(sp._pMutex)
{
AddRefCount();
}
Shared_ptr<T>* operator=(const Shared_ptr<T>& sp){
if (this != sp){
Realse();
_ptr = sp._ptr;
_pRefrCount = sp._pRefrCount;
_pMutex = sp._pMutex;
AddRefCount();
}
return this;
}
T& operator*(){ return *_ptr; }
T* operator->(){ return _ptr; }
int UseCount(){ return *_pRefrCount; }
T* Get(){ return _ptr; }
void AddRefCount(){
_pMutex.lock();
++(*_pRefrCount);
_pMutex.unlock();
}
private:
void Realse(){
bool flag = false;
_pMutex.lock();
if (--(*_pRefrCount) == 0){
delete _pRefCount;
delete _ptr;
flag = true;
}
_pMutex.unlock();
if (flag == true);
delete _pMutex;
}
private:
T* _ptr;
int* _pRefrCount;
mutex _pMutex;
};
weak_ptr指针
weak_ptr是配合shared_ptr而引入的一种智能指针来协助shared_ptr工作,它可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用记数的增加或减少。没有重载*和->但可以使用lock获得一个可用的shared_ptr对象。
实例:
#include<iostream>
#include<memory>
using namespace std;
struct ListNode
{
int _data;
weak_ptr<ListNode> _prev;
weak_ptr<ListNode> _next;
~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
system("pause");
return 0;
}
总结:
- 智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。
- auto_ptr智能指针:当对象拷贝或者赋值后,前面的对象就悬空了。
- unique_ptr智能指针:防止智能指针拷贝和复制。
- shared_ptr智能指针:通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
- weak_ptr智能指针:可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用记数的增加或减少。
- 对shared_ptr进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过make_shared函数或者通过构造函数传入普通指针。并可以通过get函数获得普通指针。
- Redis密码设置与访问限制(网络安全)
- Elasticsearch-sql 用SQL查询Elasticsearch
- python使用装饰器@函数式化django开发
- python 设计模式,“多”例模式
- 怎样把微信聊天记录导出备份到电脑【微信公众平台技巧】
- zookeeper curator选主(Leader)
- zookeeper curator使用caches实现各种监听
- python apschedule安装使用与源码分析
- 数据迁移过程中hive sql调优
- 词序:神经网络能按正确的顺序排列单词吗?
- 使用spark对hive表中的多列数据判重
- 如何从微信公众平台上下载关注用户(备份微信关注用户)
- 使用hive客户端java api读写hive集群上的信息
- 大数据算法设计模式(1) - topN spark实现
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Nginx 限制资源的访问
- 如何设计一个秒杀系统
- Linux下登录凭证窃取技巧
- kali修改root密码
- PPT画成这样,述职答辩还能过吗?
- 详解 Cookie,Session,Token
- SSM 单体框架 - 前端开发:课程和广告模块
- 80 行 Python 代码写个图形计算器
- 近期对libatapp的一些优化调整(增加服务发现和连接管理,支持yaml等)
- SSM 单体框架 - 前端开发:用户和权限模块
- Cnitch:一款Docker引擎运行进程权限检测工具
- SAP Spartacus BrowserPlatformLocation的初始化逻辑
- 重启 CentOS 7 系统后的 IP 地址问题
- SSM 单体框架 - 前端开发:用户权限控制,Nginx 和项目部署与发布
- 用 Python 制作飞机大战小游戏