动态内存分配与智能指针

内存分配:

静态存储区:

  1. 局部static对象
  2. 类的static数据成员
  3. 定义在任何函数之外的变量

栈区:

  1. 函数内的非static对象

动态内存分配的方式有:

  1. new和delete
  2. 智能指针(shared_ptr、unique_ptr、weak_ptr)
  3. allocator类
  4. malloc和free

直接管理内存:

运算符new分配内存,delete释放new分配的内存。

int* p = new int ();//new表达式在自由空间构造一个对象,并返回指向该对象的指针。
delete p;
int* pi = new int[10]();//分配一个10个int的数组,pi指向第一个int。

默认情况下,动态分配的对象是默认初始化的,这意味着内置类型或者组合类型的对象的值是未定义的,而类类型对象将用默认构造函数进行初始化
new分配const对象是合法的,但const对象必须进行初始化
如果一个程序用光了所有可用的内存,new表达式会失败,并抛出bad_alloc异常,该异常可以通过nothrow阻止,阻止异常抛出的new称为定位new

int* p1 = new int;//如果分配失败,new抛出std::bad_alloc。
int* p2 = new (nothrow)int;//如果分配失败,new返回一个空指针。

delete表达式也执行两个动作:销毁一个给定的指针指向的对象;释放对应的内存。
传递给delete的指针必须指向动态分配的内存,或者是一个空指针,释放一个并非new分配的内存,或者将相同的指针值释放多次,其行为是未定义的
释放动态数组时,数组中的元素按逆序销毁
释放一个const动态对象,只要delete指向它的指针即可。

const int* pci = new const int(1024);
delete pci;

直接管理内存容易犯的错:

  1. 忘记delete内存(内存泄漏)
  2. 使用已经释放掉的对象
  3. 同一块内存释放两次

空悬指针/野指针(dangling pointer):指向一块曾经保存数据对象但现在已经无效的内存的指针。
产生原因:指针变量声明时未初始化或者指针被delete或free后未置为nullptr以及指针操作超越了变量的作用范围

智能指针:

shared_ptr允许多个指针指向同一个对象
unique_ptr“独占”所指向的对象
weak_ptr指向一个shared_ptr管理的对象,它不控制指向对象的生存期。

shared_ptr:
初始化:

shared_ptr<string> p1;//默认初始化的智能指针保存着一个空指针

智能指针的使用方式与普通指针类似。解引用一个智能指针返回它指向的对象,如果在一个条件判断中使用智能指针,效果是检测它是否为空。

if (p1 && p1->empty()) {*p1 = "hi";//如果p1指向一个空string,解引用p1,将一个新值赋予string。
}

make_shared函数:
类似顺序容器的emplace成员,make_shared用其参数来构造给定类型的对象。

shared_ptr<int> pi = make_shared<int>(42);

引用计数:
每个shared_ptr都有一个关联的计数器,通常称其为引用计数。无论何时我们拷贝一个shared_ptr,或将其作为参数传递给一个函数以及作为一个函数的返回值时,它所关联的计数器就会递增。当我们给shared_ptr赋予一个新值或者是shared_ptr被销毁时,计数器就会递减。当一个shared_ptr的计数器变为0,他就会自动释放自己所管理的对象。

程序使用动态内存的三个原因:

  1. 程序不知道使用多少个对象(容器类)
  2. 程序不知道所需对象的具体类型
  3. 程序需要在多个对象间共享数据

shared_ptr与new的结合:
接受指针参数的智能指针构造函数是explicit的,因此我们不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化的形式

shared_ptr<int> p1 = new int(1024);//错误
shared_ptr<int> p1(new int(1024));//正确

如果想用shared_ptr管理动态数组,必须提供自己定义的删除器,并且不支持下标操作。

shared_ptr<int> sp(new int[10],[](int *p){delete [] p ;});
sp.reset();

正确使用智能指针的规范:

  1. 使用相同的内置指针值初始化(或reset)多个智能指针。
  2. 不delete get() 返回的指针。
  3. 不使用get()初始化或reset另一个智能指针。
  4. 如果你使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变为无效了。
  5. 如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。

unique_ptr:
当我们定义unique_ptr时,需要将其绑定到一个new返回的指针上,而且必须采用直接初始化的形式。
由于unique_ptr“独占”它指向的对象,因此unique_ptr不支持拷贝或赋值操作
虽然我们不能拷贝和赋值unique_ptr,但可以调用release或reset将指针所有权从一个(非const)unique_ptr转移给另一个unique:

unique_ptr<string> p1(new string("first"));
unique_ptr<string> p2(p1.release());//将所有权从p1转向p2,p1被置为空
unique_ptr<string> p3(new string ("second"));
//将所有权从p3转移给p2.
p2.reset(p3.release());//reset释放了p2原来指向的内存。

unique_ptr可以管理动态数组,必须在对象类型后面跟一对空方括号,支持下标操作。

unique_ptr<int[]> up(new int[10]);
up.release();//自动用delete[]销毁其指针。
for(size_t i = 0 ; i != 10 ; i++)
{up[i] = i;
}

weak_ptr:
weak_ptr不控制所指向的对象的生存期,指向一个由shared_ptr管理的对象。将一个weak_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。
由于对象可能不存在,不能使用weak_ptr直接访问对象,必须调用lock。此函数检查weak_ptr指向的对象是否仍存在。如果存在,lock返回一个指向共享对象的shared_ptr。
可以解决两个shared_ptr互相引用的问题。

allocator类:

将内存分配和对象构造分离开,提供一种类型感知的内存分配方法,他分配的内存是原始的、未构造的。
定义一个allocator对象,必须指明这个allocator可以分配的对象类型。当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐位置:

allocator<string> alloc;//可以分配string的allocator对象
auto const p = alloc.allocate(n);//分配n个未初始化的string。
auto q = p;
alloc.construct(q++);//*q为空字符串
alloc.construct(q++,10,'c');//*q为cccccccccc
alloc.construct(q++,"hi");//*q为hi
cout<<*p<<endl;//正确:使用string的输出运算符
cout<<*q<<endl;//灾难:q指向未构造的内存
while(q!=p)alloc.destroy(--q);//释放我们真正构造的string
//destory接受一个指针,对指向的对象执行析构函数。
alloc.deconstruct(p,n);//通过deallocate释放内存。
//传递给deallocate的指针不能为空,它必须指向由allocate分配的内存。而且传递给deallocate的大小参数必须与调用allocated分配内存时提供的大小参数具有一样的值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443638.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1151压力变送器型号_日本进口横河EJA530E压力变送器型号解读!

横河EJA变送器对大家来说也许不陌生&#xff0c;但是对于EJA变送器的型号很多人还不是很懂&#xff0c;因为一个全型号代表这很多参数&#xff0c;每一个字母和每一个数字背后都是一个准确的参数&#xff0c;我们在选型的时候要提供必要的参数&#xff0c;更具参数选出合适的型…

plc控制可调节阀流程图_PLC控制的水箱液位控制系统毕业论文

内容介绍原文档由会员 莎士比亚 发布论文标准WORD格式排版40页摘要在人们生活以及工业生产等诸多领域经常涉及到液位和流量的控制问题, 例如居民生活用水的供应, 饮料、食品加工, 溶液过滤, 化工生产等多种行业的生产加工过程, 通常需要使用蓄液池, 蓄液池中的液位需要维持合适…

idea继承后重新方法快捷键_idea 查看类继承关系的快捷键

类似eclipse ctrlt的快捷键,idea中是ctrlH…找到对应的类 查看类关系图…1.在想要查看的类上按 Ctrl H -> Diagrams -> Show Diagrams -> Java Class Diagrams -> Show Implementations -> Ctrl A -> 右击一下 -> Enter .…打开想要查看的接口或者类文件…

怎样在数组末尾添加数据_如何利用C++实现可变长的数组?

应该执行什么功能&#xff1f;假设我们要实现一个将自动扩展的数组类&#xff0c;是否需要实现函数&#xff1f;让我们从下面主要功能使用的功能开始&#xff0c;看看我们需要实现哪些功能。输出结果&#xff1a;0 1 2 3 40 1 2 100 4您需要做什么才能实现上述功能&#xff1f;…

Linux学习:第六章-Linux服务和进程管理

一进程查看 1psaux查看当前系统所有运行的进程 -a显示前台所有进程 -u显示用户名 -x显示后台进程 user&#xff1a; 用户名 pid&#xff1a;进程id。PID1init系统启动的第一个进程 %CPUcpu占用百分比 %MEM内存占用百分比 VSZ虚拟内存占用量KB RSS固定内存占有量 tty登录终端tty…

覆盖索引与联合索引_浅析MySQL的索引覆盖和索引下推

写在前面在MySQL数据库中&#xff0c;索引对查询的速度有着至关重要的影响&#xff0c;理解索引也是进行数据库性能调优的起点&#xff0c;索引就是为了提高数据查询的效率。今天我们来聊聊在MySQL索引优化中两种常见的方式&#xff0c;索引覆盖和索引下推索引覆盖要了解索引覆…

python3 输入输出_Python3基础之输入和输出实例分析

通常来说&#xff0c;一个Python程序可以从键盘读取输入&#xff0c;也可以从文件读取输入&#xff1b;而程序的结果可以输出到屏幕上&#xff0c;也可以保存到文件中便于以后使用。本文就来介绍Python中最基本的I/O函数。一、控制台I/O1.读取键盘输入内置函数input([prompt])&…

redis 哨兵 异步_突破Java面试(23-8) - Redis哨兵主备切换的数据丢失问题-阿里云开发者社区...

1 数据丢失的两个场景主备切换的过程&#xff0c;可能会导致数据丢失1.1 异步复制由于 master > slave的复制是异步的&#xff0c;所以可能有部分数据还没复制到slave&#xff0c;master就宕机&#xff0c;于是这些数据就丢失了1.2 脑裂导致脑裂&#xff0c;也就是说&#x…

vue 断开正在发送的请求_vue 发送请求频繁时取消上一次请求

前言&#xff1a;在项目中经常有一些场景会连续发送多个请求&#xff0c;而异步会导致最后得到展示的结果可能不是最后一次发送请求返回的结果&#xff0c;且对性能也有非常大的影响。场景&#xff1a;列表式切换商品&#xff0c;有时候上一次请求的结果非常慢&#xff0c;而我…

Linux学习:第六章-Linux网络配置

上一章的补充&#xff1a; 常见网络端口&#xff1a; 2021ftp服务文件共享 22ssh服务安全远程网络管理 23telnet服务 25smtp&#xff1a;简单邮件传输协议发信 110pop3&#xff1a;邮局协议收信 80www网页服务 3306mysql端口 3389windows终端服务 53DNS端口 /etc/services所有系…

sequelize 增加数据库字段_Node项目使用Sequelize操作数据库(一)(包括模型,增,删、改等)...

Sequelize 是一个基于 Promise 的 Node.js ORM&#xff0c;目前支持 Postgres、MySQL、SQLite 和 Microsoft SQL Server。它具有强大的事务支持&#xff0c;关联关系、读取和复制等功能。所谓ORM是指对象关系映射&#xff0c;通过使用描述对象和数据库之间映射的元数据&#xf…

django orm插入一条_如何通过django的ORM远程发布文章?

利用django的ORM可以方便的给数据库插入文章但是假如我django放在阿里云&#xff0c;那我想在本地写个插件&#xff0c;每天很方便的插入一些数据&#xff0c;最好是通过ORM的&#xff0c;因为管理起来比较方便&#xff0c;会涉及到多个站&#xff0c;可能会有200多个站&#x…

循环斐波那契数列_第五课:斐波那契数列(第一课时)

简介&#xff1a;又称黄金分割数列、因数学家列昂纳多斐波那契以兔子繁殖为例子而引入&#xff0c;故又称为“兔子数列”&#xff0c;指的是这样一个数列&#xff1a;1、1、2、3、5、8、13、21、34……在数学上&#xff0c;斐波那契数列以如下被以递推的方法定义&#xff1a;F(…

命令行 蓝牙_Ubuntu使用BlueZ驱动蓝牙dongle

蓝牙dongle即蓝牙适配器&#xff0c;一般为USB接口&#xff0c;通过USB连接到PC等设备。连接dongle后&#xff0c;PC即可使用驱动程序控制dongle连接其它蓝牙设备。本文主要介绍在Ubuntu系统中安装BlueZ的方法及蓝牙的使用。01获取BlueZBlueZ是Linux系统的官方蓝牙协议栈&#…

Java在Windows下导出xml文件到Linux服务器上

最近由于公司项目需要&#xff0c;学习了在Windows平台导出xml文件到Linux服务器上的指定目录下的方法&#xff0c;&#xff08;注&#xff1a;这里的我的Linux是在本机上装的虚拟机&#xff09;现在写下来记录一下&#xff01; 1.首先是项目截图&#xff1a; 2.主要是类&…

java peek函数_Java 8 Stream Api 中的 peek 操作

1. 前言我在 Java 8 Stream API中的 map 和flatMap 中讲述了Java8 Stream API中 map 操作和 flatMap 操作的区别。然后有小伙伴告诉我 peek 操作 也能实现元素的处理。但是你知道 map 和 peek 的区别吗&#xff1f; map 我们在开头文章已经讲过了&#xff0c;你可以去详细了解一…

iphone如何查看dns延迟_iPhone手机网速慢?1分钟教你设置DNS,网速立马翻一番

很多小伙伴在购机时选择苹果手机都是因为iOS系统的流畅度和精简性&#xff0c;但iPhone在可玩性和信号方面是不如安卓手机的。大家在使用过程中一定遇到过这样的情况&#xff0c;连接同一个无线网&#xff0c;但是苹果的网速总是要比安卓慢&#xff0c;这该怎么办呢&#xff1f…

mysql 关系_MySQL之关系

关系多对多的关系&#xff0c;如何通过mysql来表示站在老师的角度一个老师可以教多个学生&#xff0c;一个老师也可以教一个学生。站在学生的角度一个学生可以被一个老师教一个学生也可以被多个老师教结论&#xff1a;如果站在两边看都是一对多的情况&#xff0c;那么这个关系就…

mysql账号密码忘_mysql用户名密码忘记了解决方法

今天想用一下实验室服务器的mysql&#xff0c;发现不记得用户名密码了。解决方法如下&#xff1a;1. 保证服务器处于安全的状态&#xff0c;如果可以请拔掉网线...(不过我跳过了这一步&#xff0c;额)2. 修改/etc/my.cnf文件在[mysqld]的段中加入&#xff1a;skip-grant-tables…

copying mysql status_mysql慢查询copying to tmp table

windows server&#xff0c;无论修改my.ini的tmp_table_size&#xff0c;max_heap_table_size到多少&#xff0c;情况都一样。同样的表和查询语句&#xff0c;在本地运行&#xff0c;没出现慢查询。SELECTg.goods_id,g.goods_name,g.shop_price,g.goods_thumb,SUM(og.goods_num…