第12章_动态内存
动态内存与智能指针
- 默认初始化的智能指针中保存着一个空指针
- 智能指针的使用方式与普通指针类似
- 解引用一个智能指针返回它指向的对象
- 如果在一个条件判断中使用智能指针, 效果就是检测它是否为空
shared_ptr 类
- p->get(): 返回 p 中保存的指针(要小心使用, 若智能指针释放了其对象, 返回的指针所指向的对象也就消失了)
- swap(p, q) \ p.swap(q): 交换 p 和 q 中的指针
- make_shared
(args): 返回一个 shared_ptr, 指向一个动态分配的类型为 T 的对象. 使用 args 初始化此对象 - shared_ptr
p(q): p 是 shared_ptr q 的拷贝, 此操作会递增 q 中的计数器. q 中的指针必须能转换为 T* - p = q: p 和 q 都是 shared_ptr, 所保存的指针必须能互相转换. 此操作会递减 p 的引用计数, 递增 q 的引用计数; 若 p 的引用计数变为 0, 则将其管理的原内存释放
- p.use_count(): 返回与 p 共享对象的智能指针数量(可能很慢, 主要用于调试)
- p.unique(): 若 p.use_count() 为 1, 返回 true, 否则返回 false
直接管理内存
shared_ptr 和 new 结合使用
- std::shared_ptr
p(new int{1024}); - 不能将一个内置指针隐式转换为一个智能指针, 必须使用直接初始化形式
- std::shard_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 的拷贝, 为一的区别是 p 将用可调用对象 d 来代替 delete - p.reset() \ p.reset(q) \ p.reset(q, d): 若 p 是唯一指向其对象的 shared_ptr, reset 会释放此对象, 若传递了可选的参数内置指针 q, 会令 p 指向 q, 否则会将 p 置为空. 若还传递了参数 d, 将会调用 d 而不是 delete 来释放 q
智能指针和异常
unique_ptr
- std::unique_ptr
p(new int{1024}); - unique_ptr 拥有它指向的对象, 因此不支持普通的拷贝或赋值操作
- std::unique_ptr<T, D> u: 使用一个类型为 D 的可调用对象来释放指针
- std::unique_ptr<T, D> u(d): 用类型为 D 的对象 d 代替 delete
- u = nullptr: 释放 u 指向的对象, 将 u 置为空
- u.release(): u 放弃对指针的控制器, 返回指针, 并将 u 置为空
- u.reset() \ u.reset(q) \ u.reset(nullptr): 释放 u 指向的对象, 如果提供了内置指针 q, 令 u 指向这个对象; 否则将 u 置为空
- 不能拷贝 unique_ptr 的规则有一个例外: 可以拷贝或赋值一个将要销毁的 unique_ptr, 最常见的例子是从函数返回一个 unique_ptr
1
2
3
4
5
6
7
8
9std::unique_ptr<int> clone(int p) {
return std::unique_ptr<int>(new int{p});
}
std::unique_ptr<int> clone(int p) {
std::unique_ptr<int> ret(new int{p});
...
return ret;
}
weak_ptr
- weak_ptr 是一种不控制所指向对象生存期的智能指针, 它指向一个 shared_ptr 管理的对象
- 将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ptr 的引用计数
- 一旦最后一个指向对象的 shared_ptr 被销毁, 对象就会被释放, 即使有 weak_ptr 指向对象, 对象也还是会被释放
- weak_ptr
w(sp): 与 shared_ptr sp 指向相同对象的 weak_ptr, T 必须能转换为 sp 指向的类型 - w = p: p 可以是一个 shared_ptr 或一个 weak_ptr
- 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(由于对象可能不存在, 不要直接使用 weak_ptr 访问对象, 而必须调用 lock)
动态数组
new 和数组
- type *p = new type[size];
- delete [] p;
- 动态分配一个空数组是合法的
1
2char arr[0]; // error: 不能定义长度为 0 的数组
char *cp = new char[0]; // yes, 但 cp 不能解引用 - 标准库提供了一个可以管理 new 分配的数组的 unique_ptr 版本(可以使用下标运算符来访问数组中的元素)
1
2std::unique_ptr<int[]> up(new int[10]);
up.release(); - shared_ptr 不直接支持管理动态数组. 如果希望使用 shared_ptr 管理一个动态数组, 必须提供自己定义的删除器. 如果为定义删除器, 则代码是为定义的, 因为默认情况下, shared_ptr 使用 delete 销毁它所指向的对象. 如果为定义删除器, 则代码是为定义的, 因为默认情况下, shared_ptr 使用 delete 销毁它所指向的对象.(shared_ptr 未定义下标运算符, 而且智能指针类型不支持指针算术运算, 因此, 为了访问数组中的元素, 必须使用 get 获取一个内置指针去访问)
1
2std::unique_ptr<int> sp(new int[10], [](int *p){ delete [] p;});
sp.reset();
allocator 类
- new 有一些灵活性上的局限, 其中一方面表现在它将内存分配和对象构造组合在了一起. 当分配一块大内存时, 通常计划在这块内存上按需构造对象, 在此情况下, 希望将内存分配和对象构造分离
- allocator 类将内存分配和对象构造分离开来, 提供一种类型感知的内存分配方法, 它分配的内存是原始的, 未构造的.
方法
- allocator
a: 定义一个名为 a 的 allocator 对象, 它可以为类型为 T 的对象分配内存 - a.allocate(n): 分配一段原始的, 未构造的内存, 保存 n 个类型为 T 的对象
- a.deallocate(p, n): 释放从 T* 指针 p 中地址开始的内存, 这块内存保存了 n 个类型为 T 的对象; p 必须是先前由 allocate 返回的指针, 且 n 必须是 p 创建时所要求的大小. 在调用 deallocate 之前, 用户必须对每个在这块内存中创建的对象调用 destroy
- a.construct(p, args): p 必须是一个类型为 T* 的指针, 指向一块原始内存; args 被传递给类型为 T 的构造函数, 用来在 p 指向的内存中构造一个对象(为了使用 allocate 返回的内存, 必须用 construct 构造对象)
- a.destroy(p): p 为 T* 类型的指针, 此算法对 p 指向的对象执行析构函数(每个构造之后的元素需单独调用)
拷贝和填充为初始化内存的算法
- uninitialized_copy(b, e, b2)
- uninitialized_copy_n(b, n, b2)
- uninitialized_fill(b, e, t)
- uninitialized_fill_n(b, n, t)