C/C++基础----动态内存
- why
管理较难,忘记释放会内存泄漏,提早释放可能非法引用,重复释放。
为了更容易,更安全的使用动态内存,提供智能指针,其默认初始化保存一个空指针。
- what
shared_ptr允许多个指针指向同一个对象
unique_ptr独占所指向的对象
weak_ptr弱引用,指向shared_ptr所管理的对象。 <memory>
- shared_ptr和unique_ptr都支持的操作
操作 | 描述 |
---|---|
shared_ptr sp | 空智能指针 |
unique_ptr up | 同上 |
p | 用作条件判断 |
*p | |
p->mem | |
p.get() | 返回p中保存的指针,小心使用 |
swap(p, q) | 交换p和q中的指针 |
p.swap(q) |
- shared_ptr独有的操作
操作 | 描述 |
---|---|
make_shared (args) | 指向一个动态分配的T类型对象,用args初始化 |
shared_ptr p(q) | p是q的拷贝,会递增q中的计数器 |
p=q | 会递减p的引用计数,递增q的引用计数,如p的引用计数变为0,则释放原内存 |
p.unique() | p.use_count()为1则返回true |
p.use_count() | 返回与p共享对象的智能指针数量;可能很慢,主要用于调试 |
p和q保存的指针类型必须能相互转换 |
shared_ptr会自动增减引用,引用为0时会自动释放动态对象。只要有任何一个shared_ptr对象还引用内存,就不会释放它。所以在不用了之后记得删除shared_ptr元素。
程序使用动态内存的原因
- 程序不知道自己需要多少使用多少对象 容器类
- 程序不知道所需对象的准确类型
- 程序需要在多个对象之间共享数据
直接管理内存
在自由空间分配的内存是无名的,返回一个指向该对象的指针。
默认情况下,动态分配的对象是默认初始化的,内置类型和组合类型对象的值将是未定义的,而类类型将用默认构造函数进行初始化。
可以显式进行值初始化,在类型名后面加个括号即可。
两种初始化,主要对于内置类型以及依赖合成默认构造函数的内置类型成员差别较大。
默认初始化,它们的值是未定义的。
如提供一个括号包围的初始化器,可以使用auto定义。
auto p1 = new auto (obj); //只有当括号中是单一初始化器时可以用auto
可以用new分配const对象,相应地必须进行初始化
定位new,如分配不成功返回一个空指针
int *p = new int; //如分配失败,new抛出std::bad_alloc <new>
int *p = new (nothrow) int; //如分配失败,new返回一个空指针
delete之后置为空指针,也只是提供了有限的保护,对其他仍指向该内存的指针是无效的。
智能指针构造函数是explicit的,不能用内置指针隐式转换,必须使用直接初始化形式()。
- 定义和改变shared_ptr的其他方法
操作 | 描述 |
---|---|
shared_ptr p(q) | p管理内置指针q所指向的对象,q必须new分配,且能够转化为T*类型 |
shared_ptr p(u) | p从unique_ptr u那几关对象的所有权;u置空 |
shared_ptr p(q,d) | p管理内置指针q所指向的对象,q能够转化为T*类型,p将使用可调用对象d替代delete |
shared_ptr p(p2,d) | p是shared_ptr p2的拷贝,区别是用d替代delete |
p.reset(),p.reset(q),p.reset(q,d) | 如果p是唯一指向对象的shared_ptr,reset会释放此对象。如传递了内置指针q,会令p指向q,否则置为空。若还传递了d,会替代delete来释放q |
不要混用普通指针和智能指针
当shared绑定到一个普通指针,就将内存的管理任务交个了shared。就不应该再用内置指针来访问所指向的内存了。get函数
主要是为了这种情况设计的:当需要向不能使用智能指针的代码传递一个内置指针时。
确定代码不会delete指针。而且永远不要用get初始化另一个智能指针或为另一个智能指针赋值。改变底层对象
当要改变底层对象之前,要先检查自己是否是当前对象的唯一用户。如果不是,要先制作一份新的拷贝
if(!p.unique())
p.reset(new string(*p));
*p += newVal;//…
使用自己的释放操作替代delete,必须能够完成对shared_ptr保存指针进行释放操作。删除器必须接受一个所指对象类型的指针参数。
智能指针基本使用规范
- 不使用相同内置指针初始化或reset多个智能指针
- 不delete get()返回的指针
- 不使用delete初始化或reset另一个智能指针
- 使用get()返回的指针,在最后一个对应的智能指针销毁后,get返回的指针就无效了
- 如果使用智能指针管理的资源不是new分配的内存,记得传递一个删除器
unique_ptr
不支持拷贝赋值,定义必须绑定到一个new返回的指针上,初始化必须采用直接初始化形式。why必须new,先打个问题吧。
可以调用release或reset转移(非const)unique_ptr所有权给另一个。
unique_ptr<string> p2(p1.release());//release将p1置为空
p2.reset(p3.release());//reset释放p2原来指向的内存
release一般用来初始化或赋值另一个智能指针
- unique_ptr操作
操作 | 描述 |
---|---|
unique_ptr u1 | |
unique_ptr<T, D> u2 | D为可调用对象类 |
unique_ptr<T, D> u(d) | d为类D的可调用对象 |
u=nullptr | 释放u指向的对象,将u置为空 |
u.release() | u放弃对指针的控制权,返回指针,并将u置空 |
u.reset(),u.reset(q) ,u.reset(nullptr) | 释放u指向的对象,如果提供了内置指针q,令u指向这个对象,否则将u置为空 |
可以拷贝或赋值即将被销毁的unique,如从函数返回一个unique。
可以重载默认删除器,但是因为会影响到unique_ptr类型的构造及reset,所以必须在尖括号中提供删除器类型。
- weak_ptr
指向shared_ptr对象的弱引用,不会改变引用计数
操作 | 描述 |
---|---|
weak_ptr w | |
weak_ptr w(sp) | T必须能转换为sp指向的类型 |
w=p | p可以是sp或wp,赋值后w和p共享对象 |
w.reset() | 将w置空 |
w.use_count() | 与w共享对象的sp数量 |
w.expired() | 若w.use_count()为0则返回true |
w.lock() | 如w.expired()为true返回空sp,否则返回一个指向w的sp |
因为对象可能不存在,wp不能直接访问对象,必须先调用lock。
另外也要检查指向的对象是否非空。
为StrBlob类定义一个伴随指针类StrBlobPtr,为了访问StrBlob中私有成员,StrBlobPtr需要声明为StrBlob的友元类
- 动态数组
new 可以分配并初始化一个对象数组
allocator类,允许将分配和初始化分离,通常提供更好的性能和更灵活的内存管理能力。
由于返回的不是数组,而是数组元素类型的指针,故不能使用begin end以及范围for语句,这些操作依赖于数组维度。
同样,默认是默认初始化的,可以加一对空括号值初始化。
delete的时候,数组中的元素逆序销毁
可以使用一个unique_ptr来管理new分配的数组
unique_ptr<int[]> up (new int[10]);
up.release();//自动调用delete[]销毁
不支持成员访问运算符,支持下标,其他不变
shared_ptr不直接支持管理动态数组,除非提供一个自己定义的删除器。
shared_ptr<int> sp(new int[10], [](int *p){delete[] p;});
sp.reset();//使用lambda释放数组
sp未定义下标运算,且不支持指针的算数运算。所以访问数组元素需要使用get获取内置指针之后再用它访问元素。
allocator类及其算法
allocator类更加灵活,可以分配一块内存,在真正需要时才执行对象创建操作。
可以避免创建永远用不到的对象,而且不会使每个元素被赋值两次,一次默认初始化一次赋值时。而且没有默认构造函数的类也可以动态分配数组。
操作 | 描述 |
---|---|
allocator a | 定义一个allocator对象,可以为类型为T的对象分配内存 |
a.allocate(n) | 分配一段原始的、未构造的内存,保存n个T的对象 |
a.deallocate(p,n) | 释放p中地址开始的内存,该内存保存了n个T。p和n需跟allocate时一致,且在此之前必须先调用destroy |
a.construct(p, args) 、 p必须是T*类型指针,指向一块原始内存,args传递给构造函数,用来在内存中构造一个对象 | |
a.destroy(p) | p为T*类型指针,对p指向的对象执行析构函数。 |
allocator<string> alloc;
auto const p = alloc.allocate(n);
auto q=p;
alloc.construct(q++);
alloc.construct(q++,10,’c’);
alloc.construct(q++,”hi”);
while(q!=p)
alloc.destroy(--q);
alloc.deallocate(p,n);
- 拷贝和填充未初始化的内存
操作 | 描述 |
---|---|
uninitialized_copy(b,e,b2) | 从b e范围拷贝到b2指定的未初始化内存,b2足够大 |
uninitialized_copy_n(b,n,b2) | 从b开始拷贝n个元素到b2 |
uninitialized_fill(b,e,t) | 在b e指定的范围中创建对象,对象值均为t的拷贝 |
uninitialized_fill_n(b,n,t) | 从b指向的地址开始创建n个对象,对象值均为t的拷贝 |
auto p=alloc.allocate(vi.size() *2);
auto q=uninitialized_copy(vi.begin(), vi.end(), p);
uninitialized_fill_n(q,vi.size(),42);
C/C++基础----动态内存的更多相关文章
- 数据结构基础(1)--数组C语言实现--动态内存分配
数据结构基础(1)--数组C语言实现--动态内存分配 基本思想:数组是最常用的数据结构,在内存中连续存储,可以静态初始化(int a[2]={1,2}),可以动态初始化 malloc(). 难点就是数 ...
- 数据结构基础——指针及动态内存分配(malloc)
一.指针 C语言中的指针是一种数据类型,比如说我们用int *a;就定义了一个指针a,它指向一个int类型的数.但是这个指针是未初始化的,所以,一般的,我们都在创建指针时初始化它,以免出错,在还不吃的 ...
- C++基础之动态内存
C++支持动态分配对象,它的生命周期与它们在哪里创建无关,只有当显示的被释放时,这些对象才会被销毁.分配在静态或栈内存中的对象由编译器自动创建和销毁. new在动态内存中为对象分配空间并返回一个指向该 ...
- c++基础(六)——动态内存
在我们的程序中,静态内存——用来保存局部 static 对象,类 static数据成员,以及定义在任何函数之外的变量.栈内存——用来保存定义在函数内的非 static 对象.分配在 静态内存 或 栈 ...
- 转: Linux C 动态内存分配 malloc及相关内容 .
一.malloc()和free()的基本概念以及基本用法: 1.函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针 ...
- FreeRTOS 动态内存管理
以下转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解 FreeRTOS 动态内存管理,动态内存管理是 FreeRTOS 非常重要的一项功能,前面 ...
- Linux C 动态内存分配--malloc,new,free及相关内容
一.malloc()和free()的基本概念以及基本用法: 1.函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针 ...
- Android JNI编程(五)——C语言的静态内存分配、动态内存分配、动态创建数组
版权声明:本文出自阿钟的博客,转载请注明出处:http://blog.csdn.net/a_zhon/. 目录(?)[+] 一:什么是静态内存什么又是动态内存呢? 静态内存:是指在程序开始运行时由编译 ...
- C++中对C的扩展学习新增语法——动态内存管理
1.C语言动态内存管理的缺点: 1.malloc对象的大小需要自己计算. 2.需要手动转换指针类型. 3.C++的对象不适合使用malloc和free. 2.C++中new/delete基本使用: 3 ...
随机推荐
- Anaconda canda 安装 Python3 配置
链接: 1.安装Python 3.5以及tensorflow 以前用virtualenv觉得挺好用了,但是用多python版本下安装tensorflow,出现问题: pip is configured ...
- c# 休眠后 定时唤醒
源码见附件,执行唤醒功能前需要先对电源进行设置如下图: 源码
- $interpolateProvider
angular.module('emailParser', []) .config(['$interpolateProvider', function($interpolateProvider) { ...
- spring模拟ioc
非spring 开发 public class UserService { private UserDao userDao=new UserDaoImpl(); public void addUser ...
- java transient 知识点学习
今天看源码的时候看到这个关键字,完全没见过,不懂.好吧!学习一下. 我们都知道实现了Serilizable接口的类,便可以序列化,那么其中某些成员变量不想被序列化怎么办?就是利用transient这个 ...
- https://blog.csdn.net/qq_35447305/article/details/78587691
来源:https://blog.csdn.net/qq_35447305/article/details/78587691 需要去查看设置.C:\Users\用户名 目录下找到 .npmrc文件,删除 ...
- LG4980 【模板】Polya定理
题意 题目描述 给定一个$n$个点,$n$条边的环,有$n$种颜色,给每个顶点染色,问有多少种本质不同的染色方案,答案对$10^9+7$取模 注意本题的本质不同,定义为:只需要不能通过旋转与别的染色方 ...
- c# 自定义log4net过滤器
有时候为了实现自己想要的多个日志文件记录不同的内容,可能需要自定义log4net过滤器,比如我这里需要记录三个文件,这三个文件的内容又不能重复,多次尝试未果. 为了不更改任何现有日志代码的情况下,于是 ...
- java中的Object类和其clone()
1.Object是所有类的父类,任何类都默认继承Object,即直接或间接的继承java.lang.Object类.由于所有的类都继承在Object类,因此省略了extends Object关键字. ...
- java中的数据结构
1.链表的使用 使用时需要import java.util.List 和 java.util.ArrayList //返回list中的元素个数 int size(); //判断list中是否包含元素, ...