动态内存

文章目录
  1. 1. 动态内存和智能指针
    1. 1.1. shared_ptr类
    2. 1.2. 直接管理内存
    3. 1.3. shared_ptr和new结合使用
    4. 1.4. 智能指针和异常
    5. 1.5. unique_ptr
    6. 1.6. weak_ptr
  2. 2. 动态数组
    1. 2.1. new和数组
    2. 2.2. allocator

主要内容:

  1. 动态内存和智能指针
  2. 动态数组

到目前为止,我们程序只使用静态内存和栈内存,静态内存用来保存局部static对象、类static成员、以及定义在任何函数之外的变量。栈内存用来保存定义在函数内的非static对象。分配在静态或者栈内存中的对象由编译器自动创建和销毁,对于栈对象,仅在其定义的程序块运行时才存在。static对象在使用前分配,在程序结束是销毁。

动态内存和智能指针

shareed_ptr允许多个指针指向同一个对象;unique_ptr则独占所指的对象。weak_ptr伴随类。

shared_ptr类

智能指针也是模板,当我们创建一个智能指针时,必须提供额外的信息–指针指向的类型。

shared_ptr和unique_ptr都支持的操作 说明
shared_ptr sp unique_ptr up 空智能指针,可以指向类型为T的对象
p 将p作为一个条件判断,若p指向一个对象,则为true
*p 解引用p,获取它指向的对象
p->mem 等价于(*p)->mem
p.get 返回p中保存的指针,若智能指针释放了其对象,返回的指针所指的对象也消失了
swap(p,q) 交换pq中的指针
p.swap(q)
share_ptr独有的操作
make_shared(args) 返回一个shared_ptr,指向动态分配的类型为T的对象,使用args初始化对象
shared_ptrp(q) p 是shared_ptr q的拷贝,此操作会递q中的计算器q中的指针必须能转换为T*
p = q pq 都是shared_ptr,所保存的指针必须能相互转化,次操作会递减p的引用计数器,递增q的引用计数器,若p的引用计数器为0,则将其管理的源内存释放
p.unique() 若p.user_count为1,返回true,否则,返回false
p.use_count() 返回与p共享对象的智能指针数量,可能很慢,组要用于调试
  1. shared_ptr的拷贝和赋值
  2. shared_ptr自动销毁所管理的对象

使用动态内存处于以下三个原因:

  1. 程序不知道自己需要使用多少对象,典型例子,容器
  2. 程序不知道所需对象的准确类型
  3. 程序需要在多个对象间空闲数据

一般而言,如果。两个对象共享底层的数据,当讴歌对象被销毁时,我们不能单方面的销毁底层数据。

使用动态内存的一个常见原因是允许多个对象共享相同的状态。

直接管理内存

使用new动态分配和初始化对象:

1
int * pi = new int;

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

我们可以使用直接初始化方式来初始化一个动态分配的对象

1
string *sp = new string(10,'9');

也可以对动态分配的对象进行值初始化,只需要要类型名后跟一对圆括号即可。

对于定义了自己的构造函数的类类型来说,不论是值初始化还是默认初始化,都是使用默认构造函数,

但是对于内置类型来说,值初始化的内置类型对象有着良好定义的值,而默认初始化的对象的对象的值是未定义的。

动态分配的const对象:使用new分配const对象是合法的。一个动态分配的const对象必须初始化。

内存耗尽: 这里讲了定位new

delete之后重置指针:在delete之后,指针就变成了人们所说的空悬指针(danglind pointer)。

shared_ptr和new结合使用

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

默认情况下,一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete释放她所关联的对象。

定义和改变shared_ptr的其他方法
shared_ptrp(q) p管理内置指针q所指向的对象,q必须指向new分配的内存,且能转化为T*类型
shared_ptrp(u) p从unique_ptr哪里接管了对象的所有权,将U置位空
shared_ptep(q,d) p接管了内置指针q所指向的对象的所有权,p将使用可调用对象d来代替delete
shared_ptrp(p2,d) p是shared_ptr p2的拷贝,唯一的区别是p将用可调用对象d来代替delete
p.reset() 若p是唯一指向其对象的shared_ptr,reset将会是否次对象。
p.reset(q) 若传递了可选的参数内置指针q,会令p指向q,负责会将p置位空
p.reset(q,d) 若还传递了参数d,将会调用d而不是delete来是否q

智能指针和异常

智能指针使用规范

  1. 不能使用相同的内置指针初始化多个智能指针
  2. 不delete get()返回的指针
  3. 不适用get() 初始化或者reset另一个智能指针
  4. 如果你使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变为无效的指针了。
  5. 如果使用智能指针管理的资源不是new分配的内存,记住传递一个删除器。

unique_ptr

一个unique_ptr拥有它所指向的对象,与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定的对象。

unique_ptr操作 说明
unique_ptr u1 空unique_ptr,可以指向类型为T的对象,u1会使用delete来释放他的指针,u2 会使用一个类型为D的可调用对象来释放他的指针
unique_ptr u2
unique_ptru(d) 空的unique_ptr,指向类型为T的对象,用类型为D的对象d代替delete
u = nullptr 释放u所指的对象,将u置位空
u.release() u放弃对指针的控制权,返回指针,并将u置为空
u.reset() 释放u指向的对象
u.reset(q) 如果提供了内置指针q,令u指向这个对象,否则u置为空
u.reset(nullptr)

传递unique_ptr参数和返回unique_ptr: 不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或者赋值一个将要被销毁的unique_ptr。

weak_ptr

weak_ptr是一种不控制所指对象声明周期的指针。它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数器。

weak_ptr操作 说明
weak_ptr w 空的weak_ptr,可以指向类型为T的对象
weak_ptr w(sp) 与shared_ptr sp指向相同对象的weak_ptr,T必须能转化为sp指向的类型
w = p p 可以是一个shared_ptr 或一个weak_ptr。赋值后,w与p共享对象
w.reset() 将w置位空
w.use_count() 与w共享对象的shared_ptr的数量
w.expired() 若w.use_count()为0,返回true,否则返回false
w.lock() 如果expired为true,返回一个空的shared_ptr;否则返回一个指向w的对象的shared_ptr

动态数组

C++语言和标准库提供了一次分配一个对象数组的方法,C++中定义了另一种new表达式,可以分配并初始化一个对象数组。标准库中包含一个名为allocator的类,允许我们将分配和初始化分离。

new和数组

分配一个数组得到一个元素类型的指针: 由于分配的内存不是一个数组类型,因此不能对数组调用begin和end ,我们必须记住,动态数组并不是数组类型。

初始化动态分配数组:默认情况下使用默认初始化,可以使用圆括号进行值初始化。

allocator