C/C++基础----动态内存

  • why
管理较难,忘记释放会内存泄漏,提早释放可能非法引用,重复释放。
为了更容易,更安全的使用动态内存,提供智能指针,其默认初始化保存一个空指针。
  • what
shared_ptr允许多个指针指向同一个对象
unique_ptr独占所指向的对象
weak_ptr弱引用,指向shared_ptr所管理的对象。         <memory>
  • shared_ptr和unique_ptr都支持的操作
操作 描述
shared_ptr 空智能指针
unique_ptr 同上
p 用作条件判断
*p
p->mem
p.get() 返回p中保存的指针,小心使用
swap(p, q) 交换p和q中的指针
p.swap(q)
  • shared_ptr独有的操作
操作 描述
make_shared 指向一个动态分配的T类型对象,用args初始化
shared_ptr 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所指向的对象,q必须new分配,且能够转化为T*类型
shared_ptr p从unique_ptr u那几关对象的所有权;u置空
shared_ptr p管理内置指针q所指向的对象,q能够转化为T*类型,p将使用可调用对象d替代delete
shared_ptr 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保存指针进行释放操作。删除器必须接受一个所指对象类型的指针参数。
  • 智能指针基本使用规范

    1. 不使用相同内置指针初始化或reset多个智能指针
    2. 不delete get()返回的指针
    3. 不使用delete初始化或reset另一个智能指针
    4. 使用get()返回的指针,在最后一个对应的智能指针销毁后,get返回的指针就无效了
    5. 如果使用智能指针管理的资源不是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
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
weak_ptr 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 定义一个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);

原文地址:https://www.cnblogs.com/logchen/p/10188100.html

时间: 2024-08-29 23:19:20

C/C++基础----动态内存的相关文章

数据结构基础(1)--数组C语言实现--动态内存分配

数据结构基础(1)--数组C语言实现--动态内存分配 基本思想:数组是最常用的数据结构,在内存中连续存储,可以静态初始化(int a[2]={1,2}),可以动态初始化 malloc(). 难点就是数组在删除或者插入元素的时候,要移动元素的坐标不好确定.规律: 1.如果要在数组中第pos个位置插入一个元素(应该从后面开始移动) for( i=cnu;i>=pos;i--) pBase[i]=pBase[i-1]; 2.删除数组第pos位置的元素 for(i=pos+1;i<=cnu;i--)

C++动态内存管理好难怎么办?零基础图文讲解,小白轻松理解原理

首先我们先了解一下内存: C语言使用malloc/free动态管理内存空间,C++引入了new/delete,new[]/delete[]来动态管理内存.如果大家在自学C++中遇到困难,想找一个学习C++的环境,可以加入我的C++学习交流扣群先是513801371,能够共同学习交流和分享!![](https://s1.51cto.com/images/blog/201905/11/f3795621980960d47c291497e516b846.jpg?x-oss-process=image/w

iOS开发之c语言基础Lesson-10 动态内存管理 上课笔记 与 试题练习

//函数声明 int max(int a, int b); char * getString(); int main(int argc, const char * argv[]) { //////////////////Lesson 10 动态内存管理 课堂笔记 和 练习 ///////复习上周所学 /////////常量和变量的区别: //常量中的内容不能被修改,只能访问: //变量中存储的数据时可以随时修改的. // //const 关键字的作用: 修饰变量不可改变.当做常量使用 //  c

c++基础(六)——动态内存

在我们的程序中,静态内存——用来保存局部 static 对象,类 static数据成员,以及定义在任何函数之外的变量.栈内存——用来保存定义在函数内的非 static 对象.分配在  静态内存 或 栈内存中的对象由编译器自动创建和销毁.对于栈内存,仅在其定义的程序块运行时才存在,static对象在使用之前分配,在程序结束时销毁. 除此之外,每个程序还拥有一个内存池,这部分内存被称为自由空间(堆),程序用堆来存储动态分配的对象,——即那些在程序运行时分配的对象. 1. 动态内存和智能指针 在C++

动态内存分配类实现

今天学习了C++语言的内存动态分配,并借助所学的知识实现了一个动态内存分配类. 问题的背景为:在已经实现了一个点类的基础上,实现一个动态内存分配类,这个类 的功能为:实现根据输入的数目size,动态的生成size个点类对象:并在提供一个借口 可以对pos位置的对象进行操作:在对象生存期结束时,可以自动释放分配的内存空间. 根据描述的问题描述的需求,规划了如下的类图: 写出了一个包含三个方法的对点动态分配内存的类: 1: #ifndef _ARRAYOFPOINTS_H 2: #define _A

2015.12.14 宏定义 枚举 动态内存分配

宏定义 (#define) 宏定义的意义和用途:(习惯上,都是“k”开头) 1.为了让一些数据有意义. 2.类似于内联函数(使用简便). 3.输出日志的开关. (非零即真,0为“发布阶段”,1为“开发阶段”) 枚举 (enum) 枚举类型声明为一组相关的符号常数定义了一个类型名称.枚举用于“多项选择”场合,就是程序运行时从编译时已经设定的固定数目的“选择”中做出决定. 默认情况下,枚举中每个元素的基础类型是 int.可以使用冒号指定另一种整数值类型.默认是从0开始,后者会在前者之上+1. 动态内

FreeRTOS 动态内存管理

本章节为大家讲解 FreeRTOS 动态内存管理,动态内存管理是 FreeRTOS 非常重要的一项功能,前面章节讲解的任务创建. 信号量. 消息队列. 事件标志组. 互斥信号量. 软件定时器组等需要的 RAM 空间都是通过动态内存管理从 FreeRTOSConfig.h 文件定义的 heap 空间中申请的. 动态内存管理介绍FreeRTOS 支持 5 种动态内存管理方案,分别通过文件 heap_1,heap_2,heap_3,heap_4 和 heap_5实现,这 5 个文件在 FreeRTOS

java基础---JVM内存管理以及内存运行机制学习总结

自己从网上搜资料拼接了一张JVM内存图:如下图所示: 我们思考几个问题: 1.jVM是怎么运行的? 2.JVM运行时内存是怎么分配的? 3.我们写的java代码(类,对象,方法,常量,变量等等)最终存放在哪个区? VM运行时数据区域: 1.程序计数器(program Counter Register):   是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的 方式去实 现),字节码解释器工作时就是通过改

CH12 动态内存

动态分配的对象的生命期与它们在哪里创建的五官,只有显示地释放时,这些对象才被销毁 静态内存用来保存局部static对象.类static数据成员以及定义在任何函数之外的变量,栈内存用来保存定义在函数内的非static对象,分配在静态内存或栈内存中的对象由编译器自动创建和销毁,static对象在使用前分配,程序结束时销毁,栈对象,定义在程序块运行时才存在. 动态内存即自由空间或堆,程序用来存储动态分配的对象. 动态内存与智能指针 C++中是通过new和delete这对运算符来管理动态内存的.new: