动态内存(智能指针与new)

2023-11-07

一、引言

1、动态分配的对象的生存期与它们在哪里创建是无关的,只有当显示的被释放时,这些对象才会销毁。

2、静态内存,堆和栈内存。
2.1、静态内存是用来保存局部static对象、类static数据成员以及定义在任何函数之外的变量。
2.2、栈内存用来保存定义在函数内的非static对象。
2.3、程序用堆来存储动态内存。
2.4、分配在静态或堆内存中的对象由编译器自动创建和销毁。

3、C++中动态内存的管理是通过一对运算符来完成的。

new 在动态内存中为对象分配空间并返回一个指向该对象的指针
delete 接受一个动态对象的指针,销毁该对象,并释放与之关联的内存

注意:动态内存的使用容易出现问题。有时我们会忘记释放内存。,这种情况下就会产生内存泄漏。

4、为了更容易使用动态内存,新的标准库提供了两种智能指针来管理动态对象。

类型
shared_ptr 允许多个指针指向同一个对象
unique_ptr 独占所指向的对象
weak_ptr 一种弱引用,指向shared_ptr所管理的对象

这三种类型都定义在memory头文件中。

二、动态内存管理

1、使用动态内存的原因

1.1、程序不知道自己需要使用多少对象。
1.2、程序不知道所需对象的准确类型。
1.3、程序需要在多个对象之间共享数据。

2、智能指针

2.1、shared_ptr

2.1.1、shared_ptr定义与初始化
shared_ptr<string> p1 //shared_ptr,可以指向string
shared_ptr<list<int>> p2  //shared_ptr可以指向int的list
shared_ptr<int>p3(new int (42)); //p2指向一个值为42的int
//我们不能进行内置指针到智能指针间的隐式转换。因为智能指针默认使用delete释放它所关联的对象。

默认初始化的智能指针中保存着一个空指针。

2.1.2、shared_ptr操作
shared_ptr<~> sp 空智能指针,可以指向任意类型的对象
p 将p用作对象,若p指向一个对象,则为true
*p 解引用p,获得他指向的对象
p->mem 等价于(*p).mem
p.get() 返回p中保存的指针
swap(p,q) 交换p,q的指针
p.swap(q) 等价于swap(p,q)
make_shared<~>(args) 返回一个shared_ptr
shared_ptr<~>p(q) p是shared_ptr q的拷贝
p=q p和q都是shared_ptr,用保存的指针必须可以相互转换。此操作会递减p的引用计数,递增q的引用计数。
p.use_count() 返回与p共享对象的智能指针数量
p.unique() 若p.use_count()为1,返回true否则false
2.1.3、make_shared操作

最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。

shared_ptr<int>p3=make_shared<int>(42);  //指向一个值为42的int的shared_ptr
shared_ptr<int>p5=make_shared<int>(); //初始化值为0
shared_ptr<string> p4=make_shared<string>(10,'9'); //p4指向一个值为“9999999999”的string
auto p6=make_shared<vector<string>>();  //正确。
2.1.4、shared_ptr的拷贝和赋值

我们可以认为每个shared_ptr都有一个关联的计数器,通常称为引用计数。

递增情况:

无论何时我们拷贝一个shared_ptr,计数器都会递增。例如:用一个shared_ptr去初始化另一个shared_ptr或将它作为参数传递给一个函数或作为函数的返回值,它所关联的计数器都会递增。

递减情况:

当我们给shared_ptr赋予一个新值或shared_ptr被销毁时(例如,一个局部的shared_ptr离开其作用域)时,会递减。

一旦计数器变为0,他就会自动释放自己所管理的对象。

2.1.5、定义和改变shared_ptr的其他方式
shared_ptr<~>p(q) p管理内置指针q所指向的对象,q必须是new分配的内存
shared_ptr<~>p(u) p从unique_ptr u那里接管了对象的所有权
shared_ptr<~>p(q,d) p接管了内置指针q所指向对象的所有权,p将使用可调用对象d来代替delete
p.reset() 若p是唯一指向的对象,那么就释放p。
p.reset(q) 若传递了可选的参数内置指针q,会令p指向q,否则将p置空
p.reset(q,d) 调用d释放

2.2、unique_ptr

2.2.1、unique_ptr定义与初始化
unique_ptr<double> p1;
unique_ptr<int> p2(new int(42));

由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作。但可以传递unique_ptr参数和返回unique_ptr

unique_ptr<double> p1;
unique_ptr<int> p2(new int(42));
unique_ptr<int>p3(p2);  //错误
unique_ptr<double>p4;
p4=p1;  //错误
2.2.2、unique_ptr操作
unique_ptr<~> sp 空智能指针,可以指向任意类型的对象
p 将p用作对象,若p指向一个对象,则为true
*p 解引用p,获得他指向的对象
p->mem 等价于(*p).mem
p.get() 返回p中保存的指针
swap(p,q) 交换p,q的指针
p.swap(q) 等价于swap(p,q)
unique_ptr<T,D> u2 u2使用D来释放它的指针
unique_ptr<T,D>u(d) 用类型为D的对象d代替delete
u=nullptr 释放u指向的对象,将u置空
u.release() u放弃对指针的控制,返回指针并将u置空
u.reset() 释放u指向的对象
u.reset(q) 如果提供了内置指针q,令u指向这个对象,否则将u置空
u.reset(nullptr)

2.3、智能指针和异常

如果使用智能指针,即使程序块过早结束,智能指针类也能确保在内存不需要时将其释放。

2.4、智能指针陷阱

2.4.1、不能使用相同的内置指针初始化多个智能指针。
2.4.2、不delete get()返回的指针。
2.4.3、不使用get()初始化或者reset另一个智能指针。
2.4.4、如果使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就无效了。
2.4.5、如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。

3、new,delete

3.1、 new

3.1.1、new分配和初始化

在自由空间分配的内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针。

//采用默认初始化方式
int *p=new int;    //p指向一个动态分配的,未初始化的无名对象。
string *p1=new string;    //初始化为空string

//使用直接初始化的方式初始化动态分配的对象
int *pi=new int(1024);  //pi指向的对象的值为1024
string *p2=new string(10,'9');   //*p2=“9999999999”
vector<int> *pv=new vector<int>{0,1,2,3,4,5,6,7,8,9};

//值初始化方式
string *ps=new string();  //空字符串
int *p=new int();   // 0

//使用auto
auto p1=new auto(obj);  //正确,类型与obj类型相同
auto p2=new auto(a,b,c);  //错误,括号中只能有单个初始化器
3.1.2、new分配const对象
const int *p=new const int(1024);
const string *p1=new const string;

3.1.2.1、一个动态分配的const对象必须进行初始化。
3.1.2.2、对于一个定义了默认构造函数的类类型,其中const动态对象可以隐式初始化,而其他类型的对象必须显示初始化。
3.1.2.3、由于分配的对象是const的,new返回的指针是一个指向const的指针。

3.2、 delete

3.1.1、释放动态内存

为了防止内存耗尽,在动态内存使用完毕后,必须将其归还给系统。
delete表达式接受一个指针,指向我们要释放的对象。

int i,*pi=&i,*p2=nullptr;
double *pd=new double(33),*pd2=pd;
delete i;  //错误,i不是一个指针
delete pi;   //未定义,pi指向一个局部变量
delete pd;   //正确
delete pd2;   //未定义,pd2指向的内存已经被释放了
delete p2;  //正确

我们传递给.delete的指针必须指向动态分配的内存,或者是一个空指针。
释放一块并非new分配的内存或者将相同的指针值释放多次,其行为是未定义的。

3.1.2、释放const对象
const int *pc=new const int();
delete pc;  //正确

虽然一个const对象的值不能被改变,但他本身是可以被销毁的。

3.1.3、生存期

动态对象的生存期,直到被释放才结束,与普通对象不同。

void use_factory(T arg)
{
Foo *p=new factory(arg);
}  //p离开了他的作用域,但他所指向的内存没有被释放
3.1.4、空悬指针

当我们delete一个指针后,指针值就变为无效了,虽然指针值无效了,但是很多机器上指针仍然保存着动态内存的地址。在delete之后,指针就变成了人们常说的空悬指针。

3.3、 new,delete常见问题

3.3.1、忘记delete内存。
3.3.2、使用已经释放掉的对象。
3.3.3、同一块内存释放多次。

4、动态数组

4.1、 new和delete配合

int *pia=new int[n];  //默认初始化
int *pia1=new int[n](); //值初始化
int *pia2=new int[3]{0,1,2};  //花括号列表初始化

delete []pia;  //释放数组

4.1、 unique_ptr

为了用一个unique_ptr管理动态数组,我们必须在对象类型后面跟一对空方括号。

unique_ptr<int[]>up(new int[10]);
unique_ptr<T[ ]>u u指向一个动态分配的数组
unique_ptr<T [ ]>u§ u指向内置指针p指向的动态分配的数组
u[ i ] 返回u中拥有位置i元素值
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态内存(智能指针与new) 的相关文章

  • 在 C++ 中,当我将值传递给函数时,它是否总是转换为适当的类型?

    如果我有一个像这样的函数void func size t x 我称该函数为func 5 5 立即转换为size t类型 这通常适用于所有类型吗 我问这个问题是因为我发誓我见过人们编写代码 他们做类似的事情func 5 0 将 5 作为双精度
  • Windows CE 6.0 和运行时链接到调试 DLL /MDd

    我在 x86 PC 上使用 Windows CE 6 0 R3 我已经为该平台构建了 NK bin 和 SDK 但我有一些问题需要了解如何使用 MTd 调试 DLL 构建控制台应用程序 如果我尝试构建这个 main c with MDd i
  • 如何自定义 ASP.Net Core 模型绑定错误?

    我只想从我的 Web API Asp net Core 2 1 返回标准化的错误响应 但我似乎不知道如何处理模型绑定错误 该项目刚刚从 ASP NET Core Web 应用程序 gt API 模板创建 我有一个简单的操作定义为 Route
  • ASP.NET 数据集 getdataBy 无法启用约束。一行或多行包含违反非空、唯一或外键约束的值

    你好 我有一个非常简单的网络表单 我在此表单上有一个按钮和一个网格视图 以及一个包含链接表 bill docket docket bill 等的数据集 在按钮上单击我使用以下代码 protected void button click ob
  • 使用 CMake 对 SDL 的未定义引用

    我正在使用 SDL v1 2 15 7 和 CMake 3 2 1 开发一个项目 在 h 文件中我添加了 include
  • 如何(可移植地)在 C 和 C++ 中获取 DBL_EPSILON

    我正在 Linux AS 3 上使用 GCC 3 4 并试图找出DBL EPSILON 或者至少是一个不错的近似值 我怎样才能以编程方式获取它 在 C 中是std numeric limits
  • 使用 Process.Start() 打开文件夹时访问被拒绝异常

    我有一个 C 中的 winforms 应用程序 我必须在其中打开某个文件夹 我用 System Diagnostics Process Start pathToFolder 这会导致以下异常 System ComponentModel Wi
  • C++ Linux GCC 应用程序中的 GUID

    我有很多服务器运行这个 Linux 应用程序 我希望他们能够生成一个碰撞概率较低的 GUID 我确信我可以从 dev urandom 中提取 128 个字节 这可能没问题 但是有没有一种简单易用的方法来生成与 Win32 更等效的 GUID
  • 是否可以获取指向装箱非托管值类型的指针?

    是否可以获取指向装箱非托管值类型的指针 而无需编写对每个支持的类型进行强制转换的大型 switch 语句 就像是 object val Contains a boxed unmanaged value such as int long by
  • 为什么 .Net 框架指南建议您不要使用 ref/out 参数?

    显然 他们很 混乱 这是认真的原因吗 你还能想到其他的吗 你见过有多少开发人员并不真正理解 ref out 吗 我在真正需要的地方使用它们 但在其他地方则不然 它们通常仅在您想有效返回两个或多个值时才有用 在这种情况下它至少值得thinki
  • Math.Sin、Math.Cos 和 Math.Tan 精度以及正确显示它们的方法

    我正在用 C 编写一个计算器 textBoxResult是一个文本框 我在其中显示数字 recount是以度为单位获取角度并以弧度为单位返回的函数 我的角度是从texBoxInput public double recount int nu
  • 在 C++ 中初始化指针

    可以在声明时将指针分配给值吗 像这样的东西 int p 1000 是的 您可以在声明时初始化指向值的指针 但是您不能这样做 int p 1000 是个地址运算符 并且您不能将其应用于常量 尽管如果可以 那会很有趣 尝试使用另一个变量 int
  • 使用 C# 和 .NET Core 在 AWS Cognito 用户池中进行用户管理

    如何使用 C 和 NET Core 3 x 管理 AWS Cognito 用户池中的用户 在文档中找不到有关它的任何内容 Attilio Gelosa 的原创文章 我写这篇文章是希望对其他人有帮助 我必须阅读一页又一页的文档 并从 AWS
  • C++ 中类型信息何时向后流动?

    我刚刚看了 Stephan T Lavavej 的演讲CppCon 2018关于 类模板参数推导 在哪里某个点 https youtu be H ut6j1BYU t 941他顺便说 在 C 中 类型信息几乎永远不会向后流动 我不得不说 几
  • 当 C 中没有足够的内存用于静态分配时会发生什么?

    当您动态分配内存时 例如malloc 1024 sizeof char 结果指针设置为NULL如果没有足够的可用内存来满足请求 当没有足够的内存来满足静态分配时会发生什么 例如char c 1024 char c 1024 不一定是静态分配
  • 如何在 C 预处理器中可靠地检测 Mac OS X、iOS、Linux、Windows? [复制]

    这个问题在这里已经有答案了 如果有一些跨平台 C C 代码需要在 Mac OS X iOS Linux Windows 上编译 我如何在预处理器过程中可靠地检测到它们 大多数编译器都使用预定义的宏 您可以找到列表here http sour
  • C# 或 Windows 相当于 OS X 的 Core Data?

    我迟到了 现在才开始在 OS X Cocoa 中使用 Core Data 它令人难以置信 并且确实改变了我看待事物的方式 C 或现代 Windows 框架中是否有等效的技术 即拥有可免费保存 数据管理 删除 搜索的托管数据类型 还想知道Li
  • 更改成员资格、角色等的默认连接字符串

    默认情况下 我的网络应用程序似乎正在使用LocalSqlServer作为用于任何应用程序服务 例如成员资格 角色 身份验证 等 的连接字符串 有什么方法可以更改默认连接字符串应该是什么 默认值是 LocalSqlServer 似乎很随意 我
  • 在标准 C 中将 int 转换为 string

    我是 C 新手 我正在寻找一个可以调用函数进行转换的示例int串起来 我发现itoa但这不是标准 C 的一部分 我还发现sprintf str d aInt 但问题是我不知道所需的 str 的大小 因此 我如何传递输出字符串的正确大小 有多
  • 频繁插入已排序的集合

    我已经对集合 列表 进行了排序 并且我需要始终保持其排序 我目前在我的集合上使用 List BinarySearch 然后在正确的位置插入元素 我也尝试过在每次插入后对列表进行排序 但性能不可接受 有没有一种解决方案可以提供更好的性能 也许

随机推荐

  • 【05】MySQL:日志管理

    写在前面的话 日志是作为用户排查服务问题的重要依据 在 MySQL 中日志可以分为几类 各自产生着不同的作用 如 error log bin log slow log 等 很多时候优化数据库的优化来源就是日志 错误日志 error log
  • JAVA-数组

    数组 概念 用于存储具有相同数据类型的容器称之为数组 可以使用统一的标识符 变量名进行管理 数据既可以存储基本数据类型也可以存储引用数据类型 可以存储任意类型的数据 数组的使用 声明 声明 与变量声明类似 在相应位置声明一个变量用于存储指定
  • 16. 线性代数 - 矩阵的性质

    文章目录 神经网络的矩阵 向量 矩阵的性质 Hi 你好 我是茶桁 根据上一节课的预告 咱们这节课要进入神经网络中 看看神经网络中的矩阵 向量 然后再来详细了解下矩阵的性质 毕竟咱们的课程并不是普通的数学课 而是人工智能的数学基础 那为什么人
  • java按钮新建窗口_java中如何创建一个登录窗口,有一个按钮(或是单选框)为不再? 爱问知识人...

    建一个本地配置文件保存参数 以后每次读取 登录时如果打钩了 写入不再显示的参数 public void writeinfo throws IOException File file new File c info inf if file e
  • MySQL约束

    概述 什么是MySQL约束 约束是作用于表中字段上的规则 用于限制存储在表中的数据 约束有什么作用 保证数据库中数据的正确 有效性和完整性 分类 约束 描述 关键字 非空约束 限制该字段的数据不能为null NOT NULL 唯一约束 保证
  • 军衔系统与服务器人数,经验越打越少?CSGO个人资料军衔(等级)介绍

    本文将为CSGO玩家们详细介绍CSGO个人资料军衔 经验等级 系统 包括解释为什么经验越打越少 CSGO个人资料军衔系统于2015年5月26日随血猎大行动一同引入 玩家可以在官方服务器任何模式游戏获得经验 XP 并升级 与水平组等级 段位
  • Ant-design-vue框架学习。

    1 安装教程 npm install ant design vue save 2 运用vue cli3 0版本搭建脚手架 3 样式布局layout插件布局快速实现整体布局 4 lib flexible实现屏幕适配 安装 npm instal
  • Tomcat

    关于Tomcat和Tomcat的面试问题 一 Tomcat的缺省是多少 怎么修改 Tomcat的缺省端口号是8080 修改Tomcat端口号 1 找到Tomcat目录下的conf文件夹2 进入conf文件夹里面找到server xml文件3
  • Java线程池ThreadPoolExecutor应用(Spring Boot微服务)

    记录 475 场景 在Spring Boot微服务中使用Java线程池ThreadPoolExecutor 实现Runnable接口提交线程任务到线程池 版本 JDK 1 8 Spring Boot 2 6 3 1 使用注解配置线程池Thr
  • EMC定义 +EMC问题定位整改

    参考链接 强烈推荐 1 有源医疗器械电磁兼容入门知识汇总 2 电磁兼容EMC测试 电磁兼容整改 EMC整改 深圳第三方检测认证机构 3 EMC设计和整改 定位 专题 最重要 https blog csdn net lyh290188 cat
  • MATLAB的Structure数组

    数组的定义与调用 gt gt s struct a 1 4 7 2 9 3 Anne b James pi c magic 3 1 7 使用struct函数创建结构数组 gt gt s 1 1 a 1 2 ans 1 1 cell 数组 4
  • jdk和java什么关系_Java中JDK和JRE的区别是什么?它们的作用分别是什么?

    JDK和JRE是Java开发和运行工具 其中JDK包含了JRE 但是JRE是可以独立安装的 它们在Java开发和运行的时候起到不同的作用 1 JDK JDK是Java Development Kit的缩写 是Java的开发工具包 主要包含了
  • http的get请求如何传递一个对象

    原文链接 https www longkui site program frontend httpget 4366 0 前言 以前前台往后台对象时 后台都用POST请求 前台有时候通过拼接参数传参 会显得比较长 所以考虑前台GET请求能否直
  • Linux (Centos)下pip命令出现错误bash: pip: 命令未找到..解决方案

    今天在服务器上跑程序 提示没有XX模块 我就用pip install XX 安装了一下 结果竟然提示pip命令找不到了 pip3能安装 但是pip3 list一看 里面都没有torch包 之前应该都是用pip安装的才对 去网上找了一通 发现
  • 【算法】分支定界

    一 基本描述 类似于回溯法 也是一种在问题的解空间树T上搜索问题解的算法 但在一般情况下 分支限界法与回溯法的求解目标不同 回溯法的求解目标是找出T中满足约束条件的所有解 而分支限界法的求解目标则是找出满足约束条件的一个解 或是在满足约束条
  • React组件的生命周期

    1 组件生命周期概述 什么是组件的生命周期 组件从被创建到挂载到页面中运行 再到组件不用时卸载的过程 这个过程就叫做组件的生命周期 react在组件的生命周期中提供了一系统的钩子函数 可以让开发者在函数中注入代码 这些代码会在适当的时候运行
  • java ip解析_java域名解析

    DNS原理 http amon org dns introduction html 根域 就是所谓的 根域服务器只是具有13个IP地址 但机器数量却不是13台 因为这些IP地址借助了 域的划分 根域下来就是顶级域或者叫一级域 每个域都会有域
  • [element-ui] el-dropdown下拉菜单禁用项没有鼠标悬浮禁用样式

    鼠标移入出现禁用样式 如下图 就是我们想要的效果
  • Blender3.5 - 快捷键

    图形移动 框选 gt 刷选 gt 套索选择 W 游标 相当于形状的中心点 shitf 空格 空格 游标回到世界中心 shift C 移动 移动 G 随意移动 选中图形 G 沿 X 轴移动 选中图形 G X 沿 Y 轴移动 选中图形 G Y
  • 动态内存(智能指针与new)

    文章目录 一 引言 二 动态内存管理 1 使用动态内存的原因 2 智能指针 2 1 shared ptr 2 1 1 shared ptr定义与初始化 2 1 2 shared ptr操作 2 1 3 make shared操作 2 1 4