C++的智能指针
#pragma once
/*
Smart pointer
智能指针;灵巧指针
智能指针三大件
//1.RAII
//2.像指针一样使用
//3.拷贝问题 ,指针指针需要的是浅拷贝,并且需要处理资源释放问题 ---> 引用计数
RAII
Resource Acquisition Is Initialization 资源获取即初始化
一种利用对象的生命周期来控制资源的技术 ,是一种思想
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源
SmartPrt是RAII的一种产物
在linux下内存泄漏检测:linux下几款内存泄漏检测工具 https://blog.csdn.net/gatieme/article/details/51959654
在windows下使用第三方工具:VLD工具说明 https://blog.csdn.net/GZrhaunt/article/details/56839765
其他工具:内存泄漏工具比较 https://www.cnblogs.com/liangxiaofeng/p/4318499.html
C++11和boost中智能指针的关系
1. C++ 98 中产生了第一个智能指针auto_ptr.
2. C++ boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.
3. C++ TR1(Technical Report 1,技术引入1:草稿),引入了shared_ptr等。不过注意的是TR1并不是标准版。
4. C++ 11,引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost
的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。
*/
//智能指针常规实现1
/*
//1.RAII
//2.像指针一样使用
//3.拷贝问题 ,指针指针需要的是浅拷贝,并且需要处理资源释放问题 ---> 引用计数
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr) :_ptr(ptr)
{}
~SmartPtr()
{
if (_ptr)
delete _ptr; //同一个对象,delete一次后可以置空,之后delete 多次没关系.
// 但不同对象指向同一个资源,_ptr都是指向资源的地址,同一个非空地址delete两次就会出问题
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr; //返回地址,会优化->-> ... .当T为自定义类型时起效
}
private:
T* _ptr;
};
int div()
{
int a, b;
std::cin >> a >> b;
if (b == 0)
throw std::invalid_argument("除0错误");
return a / b;
}
void func()
{
SmartPtr<int> sp1(new int(1));
SmartPtr<int> sp2(new int(10));
*sp1 = 10;
std::cout<< *sp1<<std::endl;
std::cout<<div()<<std::endl;
}
int main()
{
try {
func();
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
return 0;
}
*/
//智能指针的发展历史
/*
C++98:auto_ptr(自动指针)
解决拷贝问题的方式:使用管理权转移方式(把资源转移给新指针,原来的指针置空)
存在的问题:资源转移后,原来的指针就成了垂悬指针.使用的人因不熟悉特性或疏忽遗漏,到导致野指针引用
//在C++11标准中已明确废弃,很多公司规范明确不能使用auto_ptr,基本上没什么价值了
#include<memory> //头文件
void func()
{
std::auto_ptr<int> sp1(new int(1)); //nullptr
std::auto_ptr<int> sp2(sp1); //sp1
*sp1 = 10; //奔溃
}
boost时期:
boost库是C++标准库的体验服,https://baike.baidu.com/item/Boost%E5%BA%93/10671694?fr=ge_ala
较为好用的智能指针
scoped_ptr
shard_ptr/weak_ptr
(
防拷贝:
C++98:
1.对拷贝构造和拷贝赋值 只声明,不实现.--- 什么都不做
2.设置为私有 --- 防止外部获取并在外部实现
C++11:
copy = delete
operator = delete
)
C++11:
unique_ptr (scoped_ptr发展)
原理:简单粗暴,防拷贝(防止拷贝) --- 解决了auto_ptr资源转移后产生的垂悬指针问题,但无法解决需要拷贝的场景
使用:不需要使用拷贝的场景就可以使用
shard_ptr/weak_ptr(boost:shard_ptr/weak_ptr发展)
原理:引用计数
线程安全: shared_ptr本身是线程安全的,但是shared_ptr管理的对象不一定是安全的
//shared_ptr简易模拟实现和使用测试
template<class T>
class shared_ptr
{
public:
//template <class U> explicit shared_ptr(U* p);
explicit shared_ptr(T* ptr = nullptr) :_ptr(ptr), _pcount(new int(1)), _pmtx(new std::mutex()) //第一次定义
{}
//with deleter(4) .接收定制删除器版本
//template <class U, class D> shared_ptr(U* p, D del);
template <class T, class D>
shared_ptr(T* ptr, D del)
: _ptr(ptr), _pcount(new int(1)), _pmtx(new std::mutex()), _del(del)
{}
shared_ptr(const shared_ptr<T>& sp) :_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx) //拷贝
{
AddCount();
}
~shared_ptr()
{
Release();
}
private:
void Release()
{
_pmtx->lock();
bool deleteFlag = false;
if (--(*_pcount) == 0)//每次执行都会--, 如果--后为0,就析构
{
if (_ptr)
{
//delete _ptr;
_del(_ptr);//把_ptr传给 通过包装器接收的 来自外界或缺省的 定制删除器
}
delete _pcount;
//锁也要释放
//{ //解决方案一
//_pmtx->unlock();
//delelte _pmtx;
//return;
//}
//解决方案二 : 使用标志位
deleteFlag = true;
}
_pmtx->unlock(); //释放锁后,不能有解锁操作
if (deleteFlag)
{
delete _pmtx;
}
}
void AddCount()
{
_pmtx->lock();
++(*_pcount);
_pmtx->unlock();
}
public:
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
//先析构掉自己的资源
if (sp._ptr != _ptr)
{
Release();
_ptr = sp._ptr;
_pcount = sp._pcount;
_pmtx = sp._pmtx; //拷贝时也要把锁烤过来
AddCount();
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr; //返回地址
}
T* get() const //getPtr
{
return _ptr;
}
int use_count() //返回引用的对象个数
{
return *_pcount;
}
private:
T* _ptr; //管理资源的指针
int* _pcount; //动态开辟的计数器,用于引用计数
std::mutex* _pmtx; //多线程时,保障临界资源的安全
std::function<void(T*)> _del = [](T*ptr) { //D是可调用对象类型,具体类型有多种,functional类型能够一个定义全部接收
std::cout << "default function<void(T*)>" << std::endl;
delete ptr; //缺省值
};
};
struct Date
{
int _year = 0;
int _month = 0;
int _day = 0;
};
void SharePtrFunc(shared_ptr<Date>& sp, size_t n, std::mutex& mtx) {
std::cout << sp.get() << std::endl;
for (size_t i = 0; i < n; ++i)
{
//验证1:验证智能指针本身是否安全,
shared_ptr<Date> copy(sp);
{
std::unique_lock<std::mutex> lk(mtx);//守护锁
copy->_year++;
copy->_month++;
copy->_day++;
//验证二: 智能指针 管理的资源不一定是线程安全的,两个线程++了2n 次是最终结果,正常情况下加了2n次,通过加锁和不加锁来验证
}
}
}
int main()
{
shared_ptr<Date> p(new Date);
std::cout << p.get() << std::endl;
const size_t n = 10000;
std::mutex mtx;
std::thread t1(SharePtrFunc, std::ref(p), n, std::ref(mtx));
std::thread t2(SharePtrFunc, std::ref(p), n, std::ref(mtx));
t1.join();
t2.join();
std::cout << p.use_count() << std::endl; //如果结果是1,说明线程安全. 否则说明不安全
std::cout<<p->_year<<std::endl;
std::cout<<p->_month<<std::endl;
std::cout<<p->_day<<std::endl;
return 0;
}
*/
//shared_prt 循环引用 问题
/*
使用C++提供的weak_ptr(弱指针),用于解决shared_ptr循环引用的问题.
weak_prt是专门用于解决shared_ptr的循环引用问题的类,特殊的智能指针,不符合RAII
weak_ptr的三个构造函数:
//1.无参构造
constexpr weak_ptr() noexcept;
//2.拷贝构造
1.weak_ptr (const weak_ptr& x) noexcept;
2.template <class U> weak_ptr (const weak_ptr<U>& x) noexcept;
//3.根据shared_ptr构造
template <class U> weak_ptr (const shared_ptr<U>& x) noexcept;
//从三个拷贝构造来看,weak_ptr没有支持使用资源来构造,说明weak_ptr不是RAII指针,不是常规的智能指针
//特点
//1.他不是常规的智能指针,不支持RAII
//2.一样像指针一样(是特殊的智能指针)
//3.专门设计出来,辅助解决shared_ptr的循环引用问题
解决循环引用问题的核心原理是不增加引用计数
//weak_ptr 简易实现,仅仅能符合简单场景,用于理解weak_ptr ---- 非常简易,库中实现很困难
template<class T>
class weak_ptr
{
public:
weak_ptr()
:_ptr(nullptr) //默认生成的不管理资源
{}
weak_ptr(const shared_ptr<T>& sp)
:_ptr(sp.get()) //帮助shared_ptr管理资源
{}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr; //返回地址
}
T* get() const //getPtr
{
return _ptr;
}
private:
T* _ptr;
};
struct ListNode
{
/.* 1.有智能指针类型不匹配问题
//ListNode* prev;
//ListNode* next;
n1->next = n2; //没有异常的正常情况下,是没有问题的
n2->prev = n1; //使用shared_ptr管理ListNode后,类型不匹配,无法转换
//需要重新封装next和prev,使用shared_ptr封装next和prev
*./
/.* 2.有循环引用问题
//shared_ptr<ListNode> _prev; //智能指针封装的list
//shared_ptr<ListNode> _next;
*./
weak_ptr<ListNode> _next;
weak_ptr<ListNode> _prev;
int _val;
~ListNode()
{
std::cout << "~ListNode()" << std::endl; //调试用 -- 看是否执行析构
}
};
void test_shared_cycle() //测试共享指针的循环问题
{
//{//一:常规写法,没有异常的正常情况下没有问题,但是异常出来以后,就不推荐这种写法
// ListNode* n1 = new ListNode;
// ListNode* n2 = new ListNode; //因为,从这里开始,如果抛异常,则n1的资源可能没有释放,--> 导致内存泄露
// n1->next = n2;
// n2->prev = n1;
// delete n1;
// delete n2;
//}
{//二:
shared_ptr<ListNode> n1(new ListNode);
//shared_ptr<ListNode> n2 = new ListNode; //隐式类型转换方式构造,调用了默认构造,使用explicit后不支持
shared_ptr<ListNode> n2{ new ListNode }; // 花括号初始化,等价于直接调用默认构造 ,注意,并不是=使用explicit后用=会报错
//shared_ptr<ListNode> n3{ new ListNode }; //花括号初始化,编译器提供的支持,
//现象1,只有n1->_obj = n2或 n2->obj = n1;时,可以正常析构
//现象2,当n1,n2循环引用时,无法正常析构了 --- shared_ptr的缺陷
n1->_next = n2;
n2->_prev = n1;
//对现象2的分析;
/.*
核心原因,两个对象相互引用:属于同一个对象的的内部资源,引用了第二个对象.而另外一个对象的内部资源,也反向引用第一个对象,
两对象互相引用,在析构时两边都在等待对方先析构,形成闭环,导致类似死锁的现象
两个对象相互引用后,引用计数都为2,析构时两个对象都为1,无法减到0.导致无法真正析构资源. --- 属于shared_ptr的缺陷
*./
//C++11对现象2的解决方案:使用weak_ptr
}
}
int main()
{
test_shared_cycle();
return 0;
}
*/
//定制删除器
/*
不同类型的资源有不同的析构方式, shared_ptr通过可调用对象:定制删除器 来实现不同方式的资源释放
定制删除器:就是可调用对象,实现一个定制的,具有删除功能的可调用对象,传给shared_ptr
template<class T>
struct DeleteArray //函数对象式定制删除器
{
void operator()(T* ptr) //shared_ptr会把资源的指针传进来
{
std::cout << " void operator()(T* ptr)" << std::endl;
delete[] ptr;
}
};
struct Date
{
int _year = 0;
int _month = 0;
int _day = 0;
};
int main()
{
shared_ptr<Date> sp0{ new Date };
shared_ptr<Date> spa1{ new Date[4],DeleteArray<Date>() }; // 函数对象式定制删除器 释放资源
shared_ptr<Date> spa2{ new Date[10],[](Date* ptr) {delete[] ptr; } }; //lambda表达式式定制删除器 释放资源
shared_ptr<FILE> spF3{ fopen("test.cpp","r"),[](FILE* ptr) {fclose(ptr); } }; //不只是堆空间,还可以是文件
return 0;
}
*/
C++的智能指针的更多相关文章
- enote笔记法使用范例(2)——指针(1)智能指针
要知道什么是智能指针,首先了解什么称为 “资源分配即初始化” what RAII:RAII—Resource Acquisition Is Initialization,即“资源分配即初始化” 在&l ...
- C++11 shared_ptr 智能指针 的使用,避免内存泄露
多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...
- C++智能指针
引用计数技术及智能指针的简单实现 基础对象类 class Point { public: Point(int xVal = 0, int yVal = 0) : x(xVal), y(yVal) { ...
- EC笔记:第三部分:17、使用独立的语句将newed对象放入智能指针
一般的智能指针都是通过一个普通指针来初始化,所以很容易写出以下的代码: #include <iostream> using namespace std; int func1(){ //返回 ...
- 智能指针shared_ptr的用法
为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...
- 智能指针unique_ptr的用法
unique_ptr是独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr,如下面错误用法: std::unique_pt ...
- 基于C/S架构的3D对战网络游戏C++框架_05搭建系统开发环境与Boost智能指针、内存池初步了解
本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...
- C++ 引用计数技术及智能指针的简单实现
一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...
- C++11智能指针读书笔记;
智能指针是一个类对象,而非一个指针对象. 原始指针:通过new建立的*指针 智能指针:通过智能指针关键字(unique_ptr, shared_ptr ,weak_ptr)建立的指针 它的一种通用实现 ...
- 「C++」理解智能指针
维基百科上面对于「智能指针」是这样描述的: 智能指针(英语:Smart pointer)是一种抽象的数据类型.在程序设计中,它通常是经由类型模板(class template)来实做,借由模板(tem ...
随机推荐
- 02uni-app v-for循环列表 v-if的使用
onLoad onShow onHide函数的使用## 这三个函数的使用 // 监听页面的加载 参数e是上一个页面传递过来的参数 参数是一个对象 如果没有为空{} onLoad(e) { consol ...
- C# Contains()、 == 和Equals() 比较
目前在开发中前端页面有搜索条件 和后端的方法进行匹配 有一个Student表,其中有字段:name.age.address.tel CREATE TABLE Student ( Name varcha ...
- YiGo学习(一)YiGo介绍
YiGo是一种开发语言,是一种面向业务人员进行管理信息系统开发的特定领域语言,属于第五代计算机语言.它可以在图形化界面上进行选择.拖拽等动作进行管理业务建模,通过建立对系统需求的描述模型由计算机自动生 ...
- 微信小程序-页面跳转wxAPI
官方文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.navigateTo.html wx.navigateTo(O ...
- Spring WebSocket实现实时通信的详细教程
简介 WebSocket 是基于TCP/IP协议,独立于HTTP协议的通信协议.WebSocket 连接允许客户端和服务器之间的全双工通信,以便任何一方都可以通过已建立的连接将数据推送到另一方. 我们 ...
- C/C++ Crypto密码库调用方法
Crypto 库是C/C++的加密算法库,这个加密库很流行,基本上涵盖了市面上的各类加密解密算法,以下代码是我在学习是总结的,放到这里用于后期需要时能够快速解决问题. 项目地址:https://www ...
- Nginx负载均衡、location匹配
nginx的日志 ``` #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$ ...
- C++文件处理工具类
C++文件处理工具类,hpp文件,使用时只需要include即可,跨平台 File.hpp #ifndef FILE_HPP #define FILE_HPP #include <fstream ...
- java 从零开始手写 redis(五)过期策略的另一种实现思路
前言 java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不丢失? ...
- 微信小程序云开发项目-个人待办事项-02今日模块开发
上一篇: 微信小程序云开发项目-个人待办事项-01介绍 https://blog.csdn.net/IndexMan/article/details/124485626 模块开发步骤 本篇介绍今日模块 ...