CPP-primer第十二章动态内存

到目前为止,我们编写的程序所使用的对象都有着严格定义的生存期:

  • 全局对象在程序启动时分配,在程序结束时销毁
  • 局部自动对象,在我们进入其定义的程序块时才被创建,在离开块时被销毁
  • 局部static对象在第一次使用前分配,在程序结束时才销毁

除了auto和static对象外,C++还支持动态分配对象,动态分配的对象生存期不受他们创建时所在的作用域限制,只有被显式释放时才会被销毁。

除了静态内存和栈内存,每个程序还拥有一个内存池,被称为堆,程序用堆来存储动态分配的对象。

动态内存和智能指针

在C++中,动态内存的管理是通过一对运算符来完成的:

  • new 在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化
  • delete 接收一个动态对象的指针,销毁该对象,并释放与之关联的内存

为了更容易地使用动态内存,C++11提供了两种智能指针来管理动态对象:

  • shared_ptr 允许多个指针指向同一个对象
  • unique_ptr 独占所指向的对象

还有一个weak_ptr,指向shared_ptr所管理的对象

具体介绍移步:CPP11特性之资源管理指针

动态数组

new和delete运算符一次分配 / 释放一个对象 ,但某些应用需要一次为很多对象分配内存的功能。例如,vector和string都是在连续内存中保存它们的元素,因此, 当容器需要重新分配内存时,必须一次性为很多元素分配内存。

为了支持这种需求,C++和标准库提供了两种一次分配一个对象数组的方法(即多个对象):

  1. C++定义了另一种 new 表达式语法,可以分配并初始化一个对象数组(原来是一个对象)。
  2. 标准库中包含一个名为 allocator 的类,允许将分配内存和初始化对象分离。 使用allocator通常会提供更好的性能和更灵活的内存管理能力,原因随后介绍。

第一种方法不介绍了,毕竟现在更愿意使用vector

  • Allocator类

    new有一些灵活性上的局限,其中一方面表现在它将内存分配和对象构造组合在了一起。类似的,delete将对象析构和内存释放组合在了一起。
    当分配一大块内存时,我们通常计划在这块内存上按需构造对象。在此情况下,我们则会希望将内存分配和对象构造分离。这意味着我们可以分配大块内存,但只在真正需要时才真正执行对象创建操作(同时付出一定开销)

    类似vector,allocator也是个类模板,为了定义一个allocator对象,必须指明这个allocator可以分配的对象类型。这样做,当一个allocator对象分配内存时,它就会根据给定的对象类型来确定恰当的内存大小和对其位置。

    1
    2
    allocator<string> alloc; // 可以分配string的allocator对象
    auto const p = alloc.allocate(n); // 分配n个未初始化的string

    当我们用完对象后,必须对每个构造的元素调用allocator的成员函数destroy来销毁它们。destroy接受一个内置指针,对指向的对象执行析构函数:

    1
    2
    while (q != p)
    alloc.destroy(--q); // 释放我们真正构造的string