第十二章 动态内存
12.1 动态内存和智能指针
1、new和delete
C++的动态内存管理是通过new和delete来完成的
new:在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化
delete:接受一个动态对象指针,摧毁该对象,并释放与之相关的内存
我们经常会忘记delete,所以推荐使用智能指针
2、三种智能指针
shared_ptr:允许多个指针指向同一个对象
unique_ptr:“独占”所指向的对象
weak_ptr:是一种弱引用,指向shared_ptr所管理的对象
这三种类型都定义在memory头文件中
3、shared_ptr类
智能指针也是模板,在使用时需要提供相应的类型
①、make_shared函数
使用该函数获得智能指针是最安全的:
②、引用计数
③、智能指针自动摧毁
当引用计数为0时,智能指针的析构函数会自动销毁对象并且释放它占用的内存
当动态对象不再被使用时,会自动释放动态对象。(共享指针被声明为局部变量)
当存在其它共享指针也指向这块内存时,它就不会被释放掉。(函数返回一个共享指针)
④、使用动态内存的三个原因
- 程序不知道自己需要使用多少对象
- 程序不知道所需对象的准确类型
- 程序需要在多个对象间共享数据
4、直接管理内存
①、使用new动态分配和初始化对象
可以直接使用直接初始化和列表初始化:
可以使用值初始化:
②、动态分配的const对象
③、内存耗尽
④、释放动态内存
- 传给delete的指针必须是动态分配的内存,或者是空指针
- 释放一块非new分配的内存,或者将相同的指针值释放多次,其行为都是未定义的
- 通常情况下,编译器不能分辨一个指针指向的是静态还是动态分配的对象。
- 编译器也不能分辨一个指针所指向的内存是否已经被释放了
因为编译器不能分辨这几种可能会发生错误的情况,所以在我们手动管理动态内存分配时需要十分注意!
⑤、动态对象的生存期到被释放时为止
两种正确写法:
⑥、动态内存管理常见的三个问题
- 忘记delete内容。这会导致内存泄漏,而且这种错误不容易找出来
- 使用已经释放掉的对象。通过在释放内存后将指针置为空,有时可以检测出这种错误
- 同一块内存释放两次。当有多个指针指向相同的动态分配对象时,可能会发生这种情况。
⑦、空悬指针
空悬指针:指向一块曾经保存数据对象但现在已经无效的内存的指针。(被delete的指针)
在delete之后将nullptr赋予指针可以避免误用空悬指针引发的错误。
5、shared_ptr和new结合使用
①、使用new返回的指针来初始化智能指针
②、不要混用普通指针和智能指针
在上面的例子中,接收了x作为参数的智能指针传进了函数。在离开函数作用域后,智能指针的引用计数变为0,该智能指针会被销毁,而智能指针指向的内存会被释放。
x指向的内存被释放后,x就变成了一个空悬指针。使用该空悬指针将会发生错误。
当一个智能指针绑定到一个普通指针时,我们就将内存的管理责任交给了这个智能指针,一旦这样做,我们就不应该再使用内置指针来访问智能指针所指向的内存了!
③、不要使用get初始化另一个智能指针或者为智能指针赋值
6、智能指针和异常
①、异常处理时智能指针的用途
有时候会漏写delete
②、为没有析构函数的类使用智能指针
③、智能指针陷阱
7、unique_ptr
relase只会返回指针,放弃对指针的控制权,并不会释放指针
unique指针不能拷贝,因为它是唯一的
存在特例情况可以拷贝unique指针:拷贝或赋值一个将要被毁灭的unique指针:
- 可以作为返回值返回
- 可以返回一个局部对象的拷贝
8、weak_ptr
弱类型指针需要绑定到共享指针上,并且不会改变共享指针的引用计数
一旦共享指针被销毁,弱指针指向的对象也会被释放。
12.2 动态数组
值得一提的时,再应用可变大小数组的场景时,容器比动态数组要更好!
1、new和数组
①、使用new分配一个对象数组
得到的动态数组并不是一个数组类型,所以不能使用begin或end,也不能使用范围for循环
②、初始化动态分配对象的数组
内置类型最好采用值初始化,否则将会是未初始化的状态。使用未初始化状态的变量将会引发错误
③、释放动态数组
④、智能指针和动态数组
类型方框中必须带有方括号[] ,这样才能说明是一个动态数组
不能使用点和箭头成员运算符
可以使用下标运算符访问数组中的元素
2、allocatir类
①、什么时候用
②、用法
allocator类定义在头文件memory中
一个简单的例子:
③、allocator分配的是未构造的内存
④、摧毁元素
⑤、释放内存
⑥、拷贝和填充未初始化内存的算法
这些函数同样是定义在memory头文件中