目录
Sec12 动态内存
12.1 动态内存与智能指针
-
new
:在动态内存中为对象分配空间,并返回一个指向该对象的指针
delete
:接受一个指针,销毁对象,释放空间 -
智能指针:
smart pointer
它负责自动释放所指向的对象-
shared_ptr
也是模板。要指定指向类型make_shared<T>(args); shared_ptr<T>p(q); // 会递增q中的计数器,q中的指针必须能转换为T* p = q; // 递减p的引用计数,递增q的引用计数,若计数为0,则释放 p.unique(); p.use_count();
-
使用例子:
auto p6 = make_shared<vector<string>>();
-
使用动态内存的原因
- 程序不知道自己需要使用多少对象
- 程序不知道所需对象的准确类型
- 程序需要在多个对象间共享数据
-
-
new和delete
-
placement new:定位new
int *p2 = new (nothrow) int;
-
delete:释放动态内存
释放一块并非new分配的内存,或者将相同的指针值释放多次,其行为是未定义的-
一定要记得释放:
反例:Foo* factory(T arg){ return new Foo(arg); } void use_factory(T arg) { Foo *p = factory(arg); // 使用了p但是没有delete } // p离开作用域,但p指向的内存没有释放!
-
建议:
坚持只使用智能指针,可以避免所有这些问题 -
delete一个指针后,指针值就变为无效了,虽然指针已经无效,但是再很多机器上指针仍然保存着已经释放了的动态内存的地址,delete之后指针变成孔璇指针,dangling pointer,即,指向一块曾经保存数据对象但现在已经无效的内存的指针
-
-
-
shared_ptr和new结合使用
可以用new返回的指针来初始化智能指针shared_ptr<T> p(q); shared_ptr<T> p(u); // uniuque_ptr u shared_ptr<T> p(q, d); // p接管了内置指针q指向的对象的所有权,而且p将使用可调用对象d,来代替delete (lambda) p.reset(); p.reset(q); p.reset(q, d);
- 不要混合使用普通指针和智能指针
-
可以用reset来将一个新的指针赋予一个shared_ptr
if(!p.unique()) p.reset(new string(*p)); // 不是唯一用户,分配新的拷贝 *p += newVal; // 唯一用户,可以改变对象的值
-
智能指针与异常
即使程序块过早结束,比如异常,也可以提早结束 -
使用我们自己的释放操作
删除器 deleter- 分配了资源,又没有定义析构函数来释放这些资源的类,可能会发生内存泄漏
-
unique_ptr
一个unique_ptr拥有它所指向的对象
某个时刻,只能有一个unique_ptr指向一个给定对象
定义的时候需要绑定给到一个new返回的指针上。且不支持普通的拷贝或赋值操作unique_ptr<string> p1<new string(":wdawdawdaw")>; unique_ptr<string> p2(p1); // 错误 unique_ptr<string> p3; p3 = p2; // 错误
-
release:
u放弃对指针的控制权,返回指针,并将u置为空返回的指针通常用来初始化或者赋值另一个智能指针
-
注:我们可以拷贝或者赋值一个将要被销毁的unique_ptr。最常见的例子是从函数返回一个unique_ptr
-
-
weak_ptr
是一种不控制所指向对象生存期的智能指针。指向一个由shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数
12.2 动态数组
-
new和数组
// 1 int *pia = new int[get_size()]; // 2 typedef int arrT[42]; int *p = new arrT;
-
初始化
int *pia = new int[10]; // 未初始化 int *pia = new int[10](); // 初始化为0 // 也可以列表初始化
-
释放动态数组
delete p; // p 指向一个动态分配的对象或者空 delete [] pa; // pa指向一个动态数组或者空 // 第二个语句销毁pa指向数组的元素,并释放对应的内存。按逆序销毁
-
-
智能指针和动态数组
标准库提供了一个可以管理new分配的数组的unique_ptr版本unique_ptr<int[]> up(new int[10]); // up指向一个包含10个未初始化int 的数组 up.release(); // 自动用delete[] 销毁其指针
注意:shared_ptr不直接支持管理动态数组/。必须提供顶一个删除器
-
allocator类
定义在头文件memory中,帮助我们将内存分配和对象构造分离开。allocator<string> alloc; // 可以分配string的allocator对象 auto const p = alloc.allocate(n); // 分配n个未初始化string
- allocator分配未构造的内存
我们按需要再此内存中构造对象。
construct成员函数接受一个指针和0个或者多个额外参数 - 用完对象后,必须对每个构造的元素调用destroy来销毁。destory接受一个指针,对指向的对象执行析构函数
- 释放内存通过调用deallocate来完成。
- allocator分配未构造的内存
-
拷贝和填充未初始化内存的算法
uninitialized_copy(b, e, b2); uninitialized_copy_n(b, n, b2); uninitialized_fill(b, e, t); uninitialized_fill_n(b, n, t);
-
使用标准库实例:文本查询程序
TextQuery