首页 > Chapter12:动态内存

Chapter12:动态内存

  • 智能指针——shared_ptr

为了更容易地使用动态内存,新的标准提供了智能指针来管理动态对象。智能指针的行为类似常规指针,重要的区别是它负责自动释放指向的对象。

 

智能指针的使用方式与普通指针类似。解引用一个智能指针返回它指向的对象。

1 if (p1 && p1->empty())

 

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

make_shared(args); //返回一个shared_ptr, 指向一个动态分配的类型为T的对象,使用args初始化此对象。

 

shared_ptr自动销毁所管理的对象,还会自动释放相关联的内存。

shared_ptr在无用之后仍然保留的一种可能是:你将shared_ptr存放在一个容器中,随后重排了容器,从而不在需要某些元素。这个时候你应该确保使用erase删除那些不再需要的shared_ptr的元素。

 

 

  • 使用了动态生存期的资源的类

(三种情况)

1. 程序不知道自己需要使用多少对象;例如容器类。

2. 程序不知道所需对象的准确类型;例如派生与继承。

3. 程序需要在多个对象间共享数据;到目前为止,我们使用过的类中,分配的资源都与对应对象生存期一致;但某些类分配的资源具有与原对象相独立的生存期,即:当某个对象被销毁时,我们不能单方面地销毁底层数据。

定义需要共享数据的类时,重点要考虑的是初始化、赋值、拷贝、销毁等拷贝控制得问题。

 

 

  • 直接管理内存:new/delete

初始化

默认情况下,动态分配的对象时默认初始化的,这意味着内置类型或组合类型的对象的值是未定义的,而类类型对象将用默认构造函数进行初始化;

对于定义了自己的构造函数的类类型来说,要求值初始化没有意义;不管采用什么形式,对象都会通过默认构造函数来初始化;

1 string *ps1 = new string; //默认初始化为空string
2 string *ps2 = new string(); //默认初始化为空string
3 
4 int* pi1 = new int;//默认初始化;*pi1的值未定义
5 int* pi1 = new int(); //值初始化为0

 

delete

释放一块并非new分配的内存,或者将相同的指针值释放多次,其行为是未定义的。

动态对象的生存期知道被释放时为止;

delete之后的指针值就变为无效了。虽然指针已经无效,但是很多机器上指针仍然保存着动态内存的地址。此时指针就变为空悬指针(dangling pointer),最好在delete之后将nullptr赋予指针。

动态内存的一个基本问题是:可能多个直着你指向相同的内存,而在实际系统中,查找相同内存的所有指针是异常困难的。

 

 

  • shared_ptr+new

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

1 shared_ptr<int> p1 = new int(1024);//错误,必须使用直接初始化形式
2 shared_ptr<int> p2(new int(1024));//正确,使用直接初始化形式

不要混合使用普通指针和智能指针——以防止同一块内存绑定到多个独立创建的shared_ptr上——推荐使用make_shared,而不是new。

 1 void process(shared_ptr<int> ptr)
 2 { //参数按值传递,发生拷贝操作
 3 }
 4 
 5 shared_ptr<int> p(new int(42));
 6 process(p);//在process中引用计数为2
 7 int i = *p;//引用计数为1
 8 
 9 int *x(new int(42));//x是普通指针
10 process(x);//错误
11 process(shared_ptr<int>(x));//会释放x的内存
12 int j = *x;

当将一个shared_ptr绑定到一个普通指针时,我们就将内存管理责任交给了这个shared_ptr。一旦这样做了,我们就不应该再使用内置指针来访问shared_ptr所指向的内存了;

使用一个内置指针来访问一个智能指针所负责的对象时很危险的,因为我们无法知道对象何时会被销毁。

 

不要使用get初始化另一个智能指针或为智能指针赋值——以防止同一块内存绑定到多个独立创建的shared_ptr上。

 

unique():在改变对象之前,我们要检查自己是否是当前对象的仅有的用户。如果不是,在改变之前,我们可能需要制作一份新的拷贝。

1 if (!p.unique())
2     p.reset(new string(*p));

 

智能指针使用规范

1. 不使用相同的内置指针值初始化(或reset)多个智能指针;

2. 不 delete get()返回的指针;

3. 不使用get()初始化或reset另一个智能指针;

4. 如果使用了get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变成无效了;

5. 如果你使用智能指针管理的资源不是new分配的内存,记住传递一个删除器给它。

 

 

  • 动态数组

标准库提供了一个可以管理new分配的数组的unique_ptr版本。为了用一个unique_ptr管理动态数组,我们必须在对象类型后面加一对空括号:

unique_ptr<int []> up(new int[10]>;
up.release();//自动用delete[]销毁其指针

与unique_ptr不同,shared_ptr不直接支持管理动态数组。如果希望使用shared_ptr管理动态数组,必须提供自己定义的删除器:

1 shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });
2 sp.reset();//使用lambda释放数组
1 for (size_t i = 0; i != 10; ++i)
2 {
3     up[i] = i;
4     *(sp.get() + i) = i;
5 }

 

 

  • allocator类

new有一些灵活性上的局限,其中一方面表现在它将内存分配和对象构造组合在一起。类似的,delete将对象析构和内存释放组合在一起;

当分配一大块内存时,我们通常计划在这块内存上按需构造对象。在此情况下,我们希望将内存分配和对象构造分离。这意味着我们可以分配大块内存,但只在真正需要时才执行对象创建工作。

allocator的两个伴随算法:

1 uninitialized_copy(b, e, b2);//从迭代器b和e指定的范围拷贝元素到迭代器b2指定的未构造的原始内存中。
2 uninitialized_copy_n(b, n, b2)
1 uninitialized_fill(b, e, t);//在迭代器b和e指定的原始内存范围内创建对象,值均为t的拷贝;
2 uninitialized_fill_n(b, n, t)

 

转载于:https://www.cnblogs.com/wangyanphp/p/5831109.html

更多相关:

  •   各位代码界的大佬大家好,今天跟大家分享一个在C/C++中常用,但是很危险的一串代码——*(p++)   为什么说这一行代码比较危险呢,因为对于C/C++来说,成也指针,败也指针。C/C++中指针便于我们操作一块连续的内存空间中内容,但是同时我们也要承担一些风险,比如:内存泄漏,野指针,只想垃圾数据等等。今天我们要说的就是指向垃圾数...

  • 1,一个整形数:  int a; 2,一个指向整形数的指针: int *a; 3,一个指向指针的指针,它指向的指针指向一个整形数:  int **a; 4,一个有10个整形数的数组: int a[10]; 5,一个有10个指针的数组,每个指针指向一个整形数: int *a[10]; 6,一个指向有10个整形数的数组的指针:  int...

  • 1 typedef char ListData; 2 //表示以后可以用ListData来代替char类型 3 4 typedef struct node{ //此处node,只在结构体中出现和使用 5 ListData data; 6 struct node *link; 7 }List...

  • 常用的几种数据类型: 类型标识符 说明 字节 值的范围   int 整型 4 –2,147,483,648 到 2,147,483,647 VC++中为long int类 short 短整型 2 –32,768 到 32,767   long 长整型 4 –2,147,483,648 到 2,147,4...

  • 点云PCL免费知识星球,点云论文速读。文章:DSP-SLAM: Object Oriented SLAM with Deep Shape Priors作者:Jingwen Wang Martin Runz Lourdes Agapito编译:点云PCL代码:https://github.com/JingwenWang95/DSP-S...

  • RAM缓存 新RAM缓存算法(CLFUS) 新的RAM缓存使用的创意来自许多缓存替换策略和算法,包括LRU,LFU,CLOCK,GDFS及2Q,它被命名为时钟周期内最小频繁使用大小算法CLFUS(Clocked Least Frequently Used by Size)。它避开了任何专利算法,具有如下特性: 均衡最近性(Rec...

  • MP4 |视频:AVC,1280×720 30 fps |音频:AAC,48 KHz,2 Ch |时长:2h 12m 语言:英语+中英文字幕(根据原英文字幕机译更准确)|大小解压后:560M C4D是一个有抱负的运动图形艺术家和设计师的重要工具。借助C4D,您可以使用3D对象、动态效果和动画来增强运动图形、模型和可视化效果。本课...

  • 文章目录先说问题:再说解决尝试1:尝试2(该尝试建议先在自己环境搭配对应业务测试通过后再现场尝试): 感谢 学无止境996同学的陪伴和vigourtyy美丽女友的支持,直到这个解决问题的深夜 先说问题: ceph 12.2.1生产环境:3副本 tier + 3副本data 机房在拥有业务的情况下重启集群交换机,产生如下场景...

  • 这周主要学习了java中的类和对象的知识点,发现和C++中的类和对象极为相似,对于类和对象的概念理解起来也简单。同时在自学的过程中也把类的知识重新复习巩固了一下(如类的三大特征:继承,封装和多态,构造,成员对象的访问权限,构造,无参有参函数的调用等),同时也了解到一些新的概念,比如类对象创建和引用占据堆内存和栈内存,输出对象时默认调...

  • C++11标准中可以为模板定义别名,比如 template using ptr=std::shared_ptr; //这里模板定义ptr为智能指针std::shared_ptr定义的别名 所以实际应用中可以借此来简化代码,比如 #include #include

  • 先说结论,智能指针都是非线程安全的。 多线程调度智能指针 这里案例使用的是shared_ptr,其他的unique_ptr或者weak_ptr的结果都是类似的,如下多线程调度代码: #include #include #include #include #i...

  • 文章目录unique_ptr 智能指针的实现shared_ptr 智能指针的实现指针类型转换 unique_ptr 智能指针的实现 一个对象只能被单个unique_ptr 所拥有。 #include using namespace std;/*增加模板类型,保证智能指针的类型是由传入的类型决定的*/ t...

  • 文章目录weak_ptr描述声明作用原理实现函数成员使用总结 weak_ptr描述 声明 头文件: 模版类:template class weak_ptr 声明方式:std::weak_ptr statement 作用 根据boost库的官方描述,weak_ptr是...

  • 文章目录shared_ptr描述声明作用原理实现函数使用关于shared_ptr循环引用问题 shared_ptr描述 声明 shared_ptr属于C++11特性中新加的一种智能指针,它的实现方式是模版类,头文件 template class shared_ptr 所以使用shared...