auto_ptr|unique_ptr|shared_ptr|weak_ptr|你都搞明白了吗?

前言
那么这里博主先安利一些干货满满的专栏了!
首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。
高质量干货博客汇总
https://blog.csdn.net/yu_cblog/category_12379430.html?spm=1001.2014.3001.5482
使用指针如何避免内存泄漏?
工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。ps:这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要智能指针来管理才有保证。
采用RAII思想或者智能指针来管理资源。
有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
出问题了使用内存泄漏工具检测。ps:不过很多工具都不够靠谱,或者收费昂贵。
最简单的例子,malloc之后我们就要free,但是如果程序还没有跑到free的地方就因为出问题而终止呢?如果有一个东西,可以像C++到类一样,自己会调用析构,那么我们是不是就不用自己手动去free了?
智能指针RAII思想
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句 柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的 候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处: 不需要显式地释放资源。 采用这种方式,对象所需的资源在其生命期内始终保持有效。
C++STL维护的智能指针有四种:
- auto_ptr
- unique_ptr
- shared_ptr
- weak_ptr
auto_ptr
auto_ptr的问题:当对象拷贝或者赋值后,前面的对象就悬空了
C++98中设计的auto_ptr问题是非常明显的,所以实际中很多公司明确规定了不能使用auto_ptr
unique_ptr
unique_ptr是C++11标准引入的一种独占所有权的智能指针。它提供了独特的所有权,即一个资源只能被一个unique_ptr拥有。它不能与其他unique_ptr共享或复制。这使得unique_ptr非常轻量和高效。当unique_ptr超出范围或被显式地释放时,它将自动删除其拥有的资源。
shared_ptr
shared_ptr是C++11标准引入的一种共享所有权的智能指针。它可以被多个shared_ptr实例共享,每个实例都持有一个引用计数。引用计数跟踪资源的当前拥有者数量,当最后一个shared_ptr超出范围时,它会自动释放资源。shared_ptr通过引用计数来管理资源,可以在多个地方共享和传递所有权,使其非常适合动态资源管理和循环引用的情况。
weak_ptr
weak_ptr也是C++11标准引入的一种弱引用智能指针。它与shared_ptr一起使用,但不会增加引用计数。weak_ptr只有资源的观察权,没有所有权。weak_ptr用于解决shared_ptr之间的循环引用问题,因为循环引用可能导致资源无法释放。通过检查weak_ptr是否有效,可以判断被观察的资源是否已被释放。
四种智能指针的代码实现
里面注释还提到了很多细节,大家可以都看一下。
#pragma once
#include<iostream>
using namespace std;
class A
{
public:
~A()
{
cout << "A::~A()" << endl;
}
int _a = 0;
int _b = 0;
};
namespace yufc
{
template<class T>
class auto_ptr
{
private:
T* _ptr;
public:
auto_ptr(T* ptr = nullptr)
:_ptr(ptr) {}
auto_ptr(auto_ptr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = nullptr;
}
~auto_ptr()
{
if (_ptr)
{
cout << "Delete:" << _ptr << endl;
delete _ptr;
}
}
//ap1 = ap2;
auto_ptr<T>& operator =(auto_ptr<T>& ap)
{
if (this != &ap)
{
if (_ptr)
{
cout << "Delete:" << _ptr << endl;
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = nullptr;
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
};
//unique_ptr
template<class T>
class unique_ptr
{
private:
T* _ptr;
public:
unique_ptr(T* ptr = nullptr)
:_ptr(ptr) {}
//仿拷贝
unique_ptr(unique_ptr<T>& ap) = delete;
unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;
~unique_ptr()
{
if (_ptr)
{
cout << "Delete:" << _ptr << endl;
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
};
//shared_ptr
//我们还是提供一个默认的删除器,不然删啥都得传
template<class T>
struct DefaultDeleter
{
void operator()(T* ptr)
{
delete ptr;//默认是delete
}
};
template<class T, class D = DefaultDeleter<T>> //传多一个定制删除器
class shared_ptr
{
protected:
T* _ptr;
//static int _count;
int* _pCount;
public:
shared_ptr(T* ptr = nullptr)
:_ptr(ptr)
, _pCount(new int(1)) {}
shared_ptr(shared_ptr<T>& sp)
:_ptr(sp._ptr)
, _pCount(sp._pCount)
{
(*_pCount)++;
}
void release()
{
cout << "Delete:" << _ptr << endl;
D()(_ptr);//用定制删除器释放
//delete _ptr;
delete _pCount;
}
shared_ptr<T>& operator=(shared_ptr<T>& sp)
{
if (_ptr == sp._ptr)
{
return *this;
}
if (--(*_pCount) == 0)
{
release();
}
//共管新资源,++记数
_ptr = sp._ptr;
_pCount = sp._pCount;
(*_pCount)++;
return *this;
}
~shared_ptr()
{
if (--(*_pCount) == 0)
{
release();
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
int use_count()
{
return *_pCount;
}
T* get() const
{
return _ptr;
}
};
//template<class T>
//int shared_ptr<T>::_count = 0;
//weak_ptr不是一个功能型的智能指针,是辅助型的
//它的发明是为了解决shared_ptr的循环引用问题
//STL的实现比我们这个复杂很多,他还处理了很多其他的问题
//STL的weak_ptr其实保存了引用计数,用于处理过期的问题,具体看杭哥解释
//STL中的它不增加引用计数,但是它要保存一下
template<class T>
class weak_ptr
{
protected:
T* _ptr;
public:
weak_ptr()
:_ptr(nullptr) {}
weak_ptr(const shared_ptr<T>& sp)
:_ptr(sp._ptr) {}
weak_ptr(const weak_ptr<T>& wp)
:_ptr(wp._ptr) {}
weak_ptr<T>& operator=(const shared_ptr<T>& sp)
{
_ptr = sp.get();
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
//库里面其实还提供get,提供原生指针
T* get() const
{
return _ptr;
}
};
}
//想要实现库里面那一套,还得复杂不少
//其实shared_ptr还涉及到线程安全问题,要加锁的
void test_auto_ptr()
{
std::auto_ptr<A>ap1(new A);
ap1->_a++;
std::auto_ptr<A>ap2(ap1);
//ap1->_a++;//err
//会出现拷贝对象的悬空问题
//很多公司明确规定不能使用它
std::auto_ptr<A>ap3(new A);
//注意 -- 这里不是交换 -- 是把ap3的资源转移给ap2,然后把ap2的释放掉,ap3悬空
ap2 = ap3;
//ap3->_a++;
}
void test_unique_ptr()
{
yufc::unique_ptr<A> up1(new A);
//不准拷贝了
//std::unique_ptr<A> up2(up1);
up1->_a++;
yufc::unique_ptr<A> up2;
//up2 = up1;//err
}
#if 1
void test_shared_ptr()
{
yufc::shared_ptr<A> sp1(new A);
yufc::shared_ptr<A> sp2(sp1);
yufc::shared_ptr<A> sp3(sp1);
yufc::shared_ptr<int> sp4(new int(1));
yufc::shared_ptr<A>sp5(new A);
}
#endif
/**
* 能否用静态变量来计数?
* 我们期望是 针对不同空间,我们希望有多个记数,但是如果我们使用了静态变量来记数
* 所有类型,所有空间的记数都堆在一起了.
*/
//weak_ptr
//循环引用
#if 0
struct Node
{
int _val;
yufc::shared_ptr<Node> _next;
yufc::shared_ptr<Node> _prev;
~Node()
{
cout << "Node::~Node()" << endl;
}
};
struct Node2
{
int _val;
yufc::weak_ptr<Node2> _next;
yufc::weak_ptr<Node2> _prev;
~Node2()
{
cout << "Node::~Node()" << endl;
}
};
#endif
#if 0 //stl的weak_ptr测试
void test_weak_ptr()
{
cout << "before use weak_ptr to construct the Node" << endl;
std::shared_ptr<Node>n1(new Node);
std::shared_ptr<Node>n2(new Node);
//tips:shared_ptr的构造是加了explicit的 -- 不允许隐式类型转换,所以不能这样写
//std::shared_ptr<Node>n2 = new Node; //err
//我们看这种情况 -- 此时不能正确释放了 -- 为什么?
cout << n1.use_count() << endl;
cout << n2.use_count() << endl;
n1->_next = n2;
n2->_prev = n1;
cout << n1.use_count() << endl;
cout << n2.use_count() << endl;
//这里就是循环引用的问题
//shared_ptr是无法解决这个问题的
//weak_ptr不是常规的智能指针,没有RAII,不支持直接管理资源
//weak_ptr就是shared_ptr的小跟班 -- 专门帮忙处理shared_ptr的剩余问题
//weak_ptr主要用shared_ptr构造 -- 处理循环引用问题
//当我们把Node里面的_prev和_next改成weak_ptr之后,_prev和_next,就不增加记数
//它不参与资源释放管理,可以访问和修改资源,不存在循环引用问题
cout << "after use weak_ptr to construct the Node" << endl;
std::shared_ptr<Node2>n11(new Node2);
std::shared_ptr<Node2>n21(new Node2);
cout << n11.use_count() << endl;
cout << n21.use_count() << endl;
n11->_next = n21;
n21->_prev = n11;
cout << n11.use_count() << endl;
cout << n21.use_count() << endl;
}
//yufc的weak_ptr测试
void test_weak_ptr2()
{
cout << " ----- before use weak_ptr to construct the Node ----- " << endl;
yufc::shared_ptr<Node>n1(new Node);
yufc::shared_ptr<Node>n2(new Node);
//tips:shared_ptr的构造是加了explicit的 -- 不允许隐式类型转换,所以不能这样写
//std::shared_ptr<Node>n2 = new Node; //err
//我们看这种情况 -- 此时不能正确释放了 -- 为什么?
cout << n1.use_count() << endl;
cout << n2.use_count() << endl;
n1->_next = n2;
n2->_prev = n1;
cout << n1.use_count() << endl;
cout << n2.use_count() << endl;
//这里就是循环引用的问题
cout << endl << endl;
//shared_ptr是无法解决这个问题的
//weak_ptr不是常规的智能指针,没有RAII,不支持直接管理资源
//weak_ptr就是shared_ptr的小跟班 -- 专门帮忙处理shared_ptr的剩余问题
//weak_ptr主要用shared_ptr构造 -- 处理循环引用问题
//当我们把Node里面的_prev和_next改成weak_ptr之后,_prev和_next,就不增加记数
//它不参与资源释放管理,可以访问和修改资源,不存在循环引用问题
cout << " ----- after use weak_ptr to construct the Node ----- " << endl;
yufc::shared_ptr<Node2>n11(new Node2);
yufc::shared_ptr<Node2>n21(new Node2);
cout << n11.use_count() << endl;
cout << n21.use_count() << endl;
n11->_next = n21;
n21->_prev = n11;
cout << n11.use_count() << endl;
cout << n21.use_count() << endl;
}
#endif
//定制删除器
struct Node
{
int _val;
std::shared_ptr<Node> _next;
std::shared_ptr<Node> _prev;
~Node()
{
cout << "Node::~Node()" << endl;
}
};
void test5()
{
std::shared_ptr<Node> n1(new Node[5]); //这里报错,因为delete[]没有匹配上
std::shared_ptr<Node> n2(new Node);
//这其实和new的底层实现是有关系的
//new[]但是delete没有[] -- 会不会报错?为什么会报错?其实这和平台有关系
//new Node[5] 这里涉及到Node的构造和析构
//此时 new Node[5] --> 5次malloc和5次构造函数
//如果是delete --> 1次析构函数+free
//delete[] --> 5次析构函数+free
//此时如果编译器发现我们的析构函数没写 -- 他会认为这个Node不需要析构 -- 直接free就行
//他会做优化 -- 因此如果析构函数不写 -- 有时候上面的情况不报错
//假设Node大小是12,现在new Node[5]
//在VS下,其实开的不是60字节,而是64字节 -- 4个字节放在头部,用来存个数
//因为delete[]其实规定不传个数 -- 所以new Node[]的时候会偷偷记录
//所以调用delete[] 就知道到底要调用多少次析构了
//所以new []返回来的指针其实比真实malloc出来的地址向后偏移了4个字节
//delete[]的时候,其实指针就会往前偏移四个字节,先找到个数
//所以delete[] 其实是free((char*)ptr - 4)这个位置的指针
//如果直接delete 首先5个Node只析构一个,其次头上的4个字节没人管了
//所以我们delete的时候要匹配
//我们用这个智能指针的时候的场景会非常复杂
std::shared_ptr<Node> n3(new Node[6]);
std::shared_ptr<int> n4((int*)malloc(sizeof(int) * 12));
//所以我们需要定制删除器
//一般是传一个仿函数或匿名参数
}
template<class T>
struct DeleteArray
{
void operator()(T* ptr)
{
cout << "delete[] " << ptr << endl;
delete[] ptr;
}
};
template<class T>
struct Free
{
void operator()(T* ptr)
{
cout << "free() " << ptr << endl;
free(ptr);
}
};
void test6()
{
//这里要看清楚文档 -- shared_ptr的定制删除器是在构造函数里面传的
//unique_ptr的地址删除器是现在模板参数里面传的
std::shared_ptr<Node> n1(new Node[5], DeleteArray<Node>());
std::shared_ptr<int> n4((int*)malloc(sizeof(int) * 12), Free<int>());
//这样就不会出问题了
std::shared_ptr<Node> n3(new Node[5], [](Node* ptr) {delete[] ptr; });//这里用匿名函数也是很好用的
std::shared_ptr<int> n2((int*)malloc(sizeof(int) * 12), [](int* ptr) {free(ptr); });
//还可以这么玩
std::shared_ptr<FILE> n5(fopen("test.txt", "w"), [](FILE* ptr) {fclose(ptr); });
//unique_ptr不能用匿名函数了,因为要在模板参数里面传对象
std::unique_ptr<Node, DeleteArray<Node>>up(new Node[5]);
}
void test7()
{
//测试我们自己定义的shared_ptr的删除器
yufc::shared_ptr<Node, DeleteArray<Node>>up(new Node[5]);
}
//如果我们要像库里面一样,可以从构造函数传,我们目前实现的架构是做不到的
//因为STL里面的计数器其实是封装成了一个类的
//我们传定制删除器的时候其实还往下传了一层
//现在我们如果要通过构造函数传,这个删除器就只能在构造函数里面用了,外面用不了。
//所以我们写一个从模板参数里面传的
//定制删除器在开始里面很少
//不过平时使用还是地用到的
auto_ptr|unique_ptr|shared_ptr|weak_ptr|你都搞明白了吗?的更多相关文章
- stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结
stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结 1. auto_ptrauto_ptr主要是用来解决资源自动释放的问题,比如如下代码:voi ...
- auto_ptr,unique_ptr,shared_ptr,weak_ptr
http://mojijs.com/2016/08/218129/index.html http://www.cnblogs.com/lanxuezaipiao/p/4132096.html
- auto_ptr, unique_ptr, shared_ptr and weak_ptr智能指针讲解
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解 ...
- 相机拍的图,电脑上画的图,word里的文字,电脑屏幕,手机屏幕,相机屏幕显示大小一切的一切都搞明白了!
相机拍的图,电脑上画的图,word里的文字,电脑屏幕,手机屏幕,相机屏幕显示大小一切的一切都搞明白了! 先说图片X×dpi=点数dotX是图片实际尺寸,简单点,我们只算图片的高吧,比如说拍了张图片14 ...
- 这20个常规Python语法你都搞明白了吗?
Python简单易学,但又博大精深.许多人号称精通Python,却不会写Pythonic的代码,对很多常用包的使用也并不熟悉.学海无涯,我们先来了解一些Python中最基本的内容. Python的特点 ...
- 就想搞明白,component-scan 是怎么把Bean都注册到Spring容器的!
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 忒复杂,没等搞明白大促都过去了! 你经历过618和双11吗?你加入过大促时候那么多复 ...
- c++智能指针的使用,shared_ptr,unique_ptr,weak_ptr
c++智能指针的使用 官方参考 普通指针的烦恼:内存泄漏,多次释放,提前释放 智能指针 负责自动释放所指向的对象. 三种智能指针 shared_ptr,unique_ptr,weak_ptr: 将sh ...
- shared_ptr & weak_ptr
shared_ptr <1> 类模板说明 namespace boost { class bad_weak_ptr: public std::exception; template< ...
- auto_ptr与shared_ptr ZZ
http://blog.csdn.net/rogeryi/article/details/1442700 Part(1) 这篇文章试图说明如何使用auto_ptr和shared_ptr,从而使得动态分 ...
- LIN、CAN、FlexRay、MOST,三分钟搞明白四大汽车总线
LIN.CAN.FlexRay.MOST,三分钟搞明白四大汽车总线 2016-09-21 13:09 汽车中的电子部件越来越多,光是ECU就有几十个,这么多的电子单元都要进行信息交互.传统的点对点通信 ...
随机推荐
- Codeforce:131A. cAPS lOCK
原题链接 ╮(╯▽╰)╭这题题目一开始没看明白,导致wa几次.如果全是大写或者出了首字母是小写其他为大写,则转换为第一个字母大写,其他的小写 ,如果不是以上两种情况则不作处理. ╮(╯▽╰)╭水题还错 ...
- spring管理实务有几种方式
一:事务认识 大家所了解的事务Transaction,它是一些列严密操作动作,要么都操作完成,要么都回滚撤销.Spring事务管理基于底层数据库本身的事务处理机制.数据库事务的基础,是掌握Spring ...
- jdk1.8:stream流式分组groupby
package com.example.apidemo.jdk18; import java.math.BigDecimal; import java.util.Arrays; import java ...
- 面试官:SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制. 那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到 ...
- C#使用ParseExact方法将字符串转化为日期格式
private void btn_Convert_Click(object sender, EventArgs e) { #region 针对Windows 7系统 string s = string ...
- 接口自动化复习第四天利用正则和faker提取替换变量值
在做接口自动化测试的时候,我们经常会遇到,有些字段利用随机生成数据就行了,不需要自己去构造测试数据.今天我就是要python中的第三方库faker来构造随机数,其次使用正则表达式来提取变量. 首先在接 ...
- ACP 知识点总结
记录下学习ACP过程不断遇到的且需要记录的知识点: 在阿里云专有网络VPC创建之后,路由器也是随着VPC一起自动创建,所以不需要手动创建,这个时候需要继续创建交换机才能在交换机种创建其他云产品. 7层 ...
- 如何安全的大数据量表在线进行DDL操作
本文为博主原创,转载请注明出处 随着业务的需要,工作中需要对生产数据库的一些表做一些DDL操作,由于生产数据库表的数据量都是几千万, 而且生产数据库的表还在不断的进行新增和查询操作.应用中需要对生产数 ...
- 在线photoshop网页版工具开发
基于javascript开发的在线ps工具,打包方式webpack 在线预览 在线ps网页版 源码地址 https://github.com/geeeeeeeek 功能介绍 在线图像编辑器允许您使用H ...
- Laravel - 模板中的url
<!-- 1, url --> <a href="{{url('/')}}">跳转到主页</a> <!-- 2,action 方法 ...