• 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保存指针进行释放操作。删除器必须接受一个所指对象类型的指针参数。
  • 智能指针基本使用规范

    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 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. 数据结构基础(1)--数组C语言实现--动态内存分配

    数据结构基础(1)--数组C语言实现--动态内存分配 基本思想:数组是最常用的数据结构,在内存中连续存储,可以静态初始化(int a[2]={1,2}),可以动态初始化 malloc(). 难点就是数 ...

  2. 数据结构基础——指针及动态内存分配(malloc)

    一.指针 C语言中的指针是一种数据类型,比如说我们用int *a;就定义了一个指针a,它指向一个int类型的数.但是这个指针是未初始化的,所以,一般的,我们都在创建指针时初始化它,以免出错,在还不吃的 ...

  3. C++基础之动态内存

    C++支持动态分配对象,它的生命周期与它们在哪里创建无关,只有当显示的被释放时,这些对象才会被销毁.分配在静态或栈内存中的对象由编译器自动创建和销毁. new在动态内存中为对象分配空间并返回一个指向该 ...

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

    在我们的程序中,静态内存——用来保存局部 static 对象,类 static数据成员,以及定义在任何函数之外的变量.栈内存——用来保存定义在函数内的非 static 对象.分配在  静态内存 或 栈 ...

  5. 转: Linux C 动态内存分配 malloc及相关内容 .

    一.malloc()和free()的基本概念以及基本用法: 1.函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针 ...

  6. FreeRTOS 动态内存管理

    以下转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解 FreeRTOS 动态内存管理,动态内存管理是 FreeRTOS 非常重要的一项功能,前面 ...

  7. Linux C 动态内存分配--malloc,new,free及相关内容

    一.malloc()和free()的基本概念以及基本用法: 1.函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针 ...

  8. Android JNI编程(五)——C语言的静态内存分配、动态内存分配、动态创建数组

    版权声明:本文出自阿钟的博客,转载请注明出处:http://blog.csdn.net/a_zhon/. 目录(?)[+] 一:什么是静态内存什么又是动态内存呢? 静态内存:是指在程序开始运行时由编译 ...

  9. C++中对C的扩展学习新增语法——动态内存管理

    1.C语言动态内存管理的缺点: 1.malloc对象的大小需要自己计算. 2.需要手动转换指针类型. 3.C++的对象不适合使用malloc和free. 2.C++中new/delete基本使用: 3 ...

随机推荐

  1. Linux 针对nginx日志文件做ip防刷限制

    针对nginx日志做ip访问限制 1.cat /var/log/server/nginx/access.log| awk -F '?' '/optionid/{print $1}'|awk '{pri ...

  2. Java中的Arrays类使用详解

    首先先创建一个打印数组的方法,方便后面直接使用 public static void output(int []a) { for(int i=0;i<a.length;i++) { System ...

  3. Map 知识整理

    首先是HashMap的学习,理解散列的概念以及相关的实现,并且会学习HashMap的源码,理解为什么HashMap的速度如此之快. 声明:参考到的资料在下方列出. 1.<Java编程思想> ...

  4. 直面Java 第003期

    . 问:什么是平台无关性,Java是如何做到平台无关的? 解: 大家说的都很对,看来大家对这个概念掌握的很可以.我简单总结一下. 跨平台指的是一种语言在计算机上的运行不受平台的约束,一次编译,到处执行 ...

  5. 【mysql】索引原理-MySQL索引原理以及查询优化

    转载:https://www.cnblogs.com/bypp/p/7755307.html 一.介绍 1.什么是索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性 ...

  6. Centos7——selinux配置

    临时关闭selinux setenforce 0 永久关闭selinux vi /etc/selinux/config 修改enforcing为disabled 情景一: httpd修改网站文件目录的 ...

  7. C# 键盘响应事件及键值对照表

    键盘响应事件总结 键盘响应事件是在用户按下某个键后触发的事件,可以是任意操作,但不是任意键都可以被捕获的. 原型:public event KeyPressEventHandler KeyPress ...

  8. c# AddMonths,你了解吗?

    AddMonths:找到对应月的day,如果没有则取最后一个day var d1 = new DateTime(2017, 6, 30); var d2 = d1.AddMonths(-1);//20 ...

  9. MT7628如何配置使用 Openwrt路由模式 (校园网配置)

    1.设置wan,把网线插入wan口 1) 在 MT7628 开发板上的 3 个网口默认都是“LAN 口”功能,但拨号上网一般需要用到“WAN口”的功能,所以我们需要将其中一个切换为“WAN 口”,这里 ...

  10. Meandering Through the Maze of MFC Message and Command Routing MFC消息路由机制分析

    Meandering Through the Maze of MFC Message and Command Routing Paul DiLascia Paul DiLascia is a free ...