【C++】动态内存(二)智能指针

由于new和delete会造成一定程度的内存泄漏问题,以及内存所有权不清晰,因此引入自动销毁相应内存空间的智能指针。

智能指针是抽象数据类型,本身具有析构函数,因此调用之后会自动调用析构函数,在析构函数中会自动调用delete来释放相应内存空间,因此不用手动显式的调用delete。

一、shared_ptr 基于引用计数的

1.基本用法:

cpp 复制代码
std::shared_ptr<int> x(new int(4));

2.get()用法:

shared_ptr是一个类模板:std::shared_ptr,其中的模板参数是T。

get()用法返回的是一个T*。

所以shared_ptr<int>返回的是一个int*类型的指针。

其实意思是这样:shared_ptr其实并不是一个单纯的指针,他是一个类模板,里面包含了指向保存数据的内存的指针,也包含了指向引用计数的指针,还包含了很多方法(也就是函数),而get方法就是返回一个什么其他东西都没有的,裸指针,这个指针只是指向那个保存数据的内存地址。

3.reset()用法:

可以将已经赋值的shared_ptr指针的值改变为其他值。

cpp 复制代码
std::shared_ptr<int> x(new int(4));
x.reset(new int(4));

也可将其赋值为nullptr,此时,下面两句是等价的。

cpp 复制代码
x.reset();//x.reset((int*)nullptr);

4.指定内存回收逻辑

当我们想在shared_ptr销毁之前做一些其他事情的时候,就不能使用隐式的delete析构了,因为我们不知道她多会儿会调用该隐式析构,故而不知道何时该做我们想做的析构之前的其他事情,这时就需要显式的自己写一个函数去调用。

如下:

cpp 复制代码
void fun2(int *ptr) {
	std::cout << "call deleter fun2" << std::endl;
	delete ptr;
}
int main()
{std::shared_ptr<int> ptr(new int(4),fun2);}

这样写,那么当shared_ptr的ptr指针的使用计数为0的时候,准备析构的时候,他不会调用默认的析构函数,而是会调用fun2函数,把自己这个地址传入给fun2函数,并且执行fun2中的代码之后由fun2中我们写的显示的析构来删除和销毁。

5.make_shared

cpp 复制代码
std::shared_ptr<int> ptr(new int(3));
std::shared_ptr<int> x = std::make_shared<int>(3);

下面这句也可以简化为:

cpp 复制代码
auto x = std::make_shared<int>(3);

上面三句的效果是一样的。但是推荐使用make_shared,而不推荐使用new。

原因:shared_ptr一共是需要维护两个指针的,一个指针指向保存数据的内存地址,一个指针是用来进行计数的,也就是引用计数,引用计数为0的时候就可以执行析构从而释放该空间了。

而使用new的时候,两个指针指向的地址可能会相隔很远,在计算机运行时偶尔会产生错误。

而使用make_shared的时候会有一个自带的优化,让shared_ptr的两个指针,即一个指向保存数据的内存地址的指针,一个指向引用计数的内存的指针,两个的内存分配的近一点,那么两个指针指的位置就会近一点,计算机相对不那么容易出现运行错误。

6.支持数组( C++17 支持 shared_ptr<T[]> ; C++20 支持make_shared 分配数组)

如下的代码是不合理的:

cpp 复制代码
std::shared_ptr<int> x = new int[6];

因为shared_ptr的隐式析构函数是delete ptr,是销毁单独的指针的,但是上面的代码右边明显是构造了一个数组,而数组对应的销毁方式应该是delete []。所以上式是不可以的,是危险的。

解决方法也是有的,在c++17中支持了:

cpp 复制代码
std::shared_ptr<int[]> x = new int[6];

c++17之后可以像左边一样这样写的时候,shared_ptr在默认析构的时候就会知道这是一个数组,而去调用delete []。,就没有危险了。

而c++20之后又支持了:

cpp 复制代码
auto x = std::make_shared<int[5]>();

在右边make_shared可以分配数组了,左边使用的是auto,其实翻译过来在编译器看来就是这样的:

cpp 复制代码
std::shared_ptr<int[]> x= std::make_shared<int[5]>();

这个也是可以默认使用delete []去析构的,也是安全的,完备的。

7.注意: shared_ptr 管理的对象不要调用 delete 销毁

shared_ptr会自动销毁,一定不要定义了之后再手动的写一个显式的delete在后面,这样会造成多次销毁一个内存空间,会造成报错。

相关推荐
坚毅不拔的柠檬柠檬17 分钟前
AI革命下的多元生态:DeepSeek、ChatGPT、XAI、文心一言与通义千问的行业渗透与场景重构
人工智能·chatgpt·文心一言
坚毅不拔的柠檬柠檬22 分钟前
2025:人工智能重构人类文明的新纪元
人工智能·重构
jixunwulian28 分钟前
DeepSeek赋能AI边缘计算网关,开启智能新时代!
人工智能·边缘计算
Archie_IT36 分钟前
DeepSeek R1/V3满血版——在线体验与API调用
人工智能·深度学习·ai·自然语言处理
Moonnnn.36 分钟前
51单片机学习——动态数码管显示
笔记·嵌入式硬件·学习·51单片机
黑子哥呢?39 分钟前
安装Bash completion解决tab不能补全问题
开发语言·bash
青龙小码农44 分钟前
yum报错:bash: /usr/bin/yum: /usr/bin/python: 坏的解释器:没有那个文件或目录
开发语言·python·bash·liunx
大数据追光猿1 小时前
Python应用算法之贪心算法理解和实践
大数据·开发语言·人工智能·python·深度学习·算法·贪心算法
Dream it possible!1 小时前
LeetCode 热题 100_在排序数组中查找元素的第一个和最后一个位置(65_34_中等_C++)(二分查找)(一次二分查找+挨个搜索;两次二分查找)
c++·算法·leetcode
夏末秋也凉1 小时前
力扣-回溯-46 全排列
数据结构·算法·leetcode