C++中内存包含静态内存,栈内存,自由空间(堆).
静态内存用于保存局部的static(静态)对象,以及定义于任何函数以外的变量(全局变量).
栈内存用来保存定义在函数内的非static对象,由编译器自动创建和销毁.
程序可以用堆来存储动态分配的对象,同时也需要由我们来显式地销毁.
12.1动态内存与智能指针
C++中动态内存的管理是由new和delete来完成的,由new来为对象分配空间并返回指向该对象的指针.由delete来对指针指向的对象进行销毁.
int *i=new int(10); //创建
cout<<*i<endl; //使用
delete i; //销毁
cout<<*i<endl; //抛出异常
新的标准库提供了两种智能指针(shared_ptr,unique_ptr),智能指针可以自动释放内存!
另外标准库还定义了名为weak_ptr的伴随类,它指向shared_ptr所管理的对象.
以上三种类型都定义在头文件memory头文件中.
12.1.1 shared_ptr类
智能指针也是模板,需提供指针可以指向的类型.
shared_ptr<string> p1; //p1是可以指向string的shared_ptr
shared_ptr<vector<int>> p2; //p2是可以指向元素类型为int的vector的shared_ptr
默认初始化的智能指针中保存空指针.
最安全的分配和使用动态内存的方法是调用来自标准库的函数make_shared,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr.我们一般用auto来接收,因为写起来比较简单.
shared_ptr<int> p1=make_shared<int>(10); //一个指向10的shared_ptr
auto p2=make_shared<vector<int>>(); //一个指向元素类型为int的空vector的shared_ptr
当shared_ptr被拷贝或是赋值时,会有一个关联的计数器(可以这么理解)来记录有多少个其他shared_ptr指向相同的对象.可以通过p.use_count()来获取查看有多少个,主要用于调试.
当我们拷贝shared_ptr时,计数器会加一.当我们给shared_ptr赋予新值或是销毁时(离开作用域),计数器会减一,当计数器为0是,shared_ptr会自动释放自己所管理的对象.
shared_ptr除了自动销毁所管理的对象,还会自动释放相关联的内存.
使用动态内存通常出于以下三种原因:
程序不知道自己需要多少对象.
程序不知道所需对象的准确类型.
程序需要在多个对象间共享数据.
12.1.2直接管理内存
可以使用new来分配const对象,并且必须进行初始化.分配的是const对象时,new返回的是指向const的指针.
如果我们的自由空间耗尽,那么new会失败,并且抛出异常.我们可以改变new的方式来阻止抛出异常:
int *p=new (nothrow) int; //如果分配失败,那么p为空指针(nullptr)而不是抛出异常
通常情况下,编译器无法分辨指针的指向,因此在释放内存前最好是先判断一下要释放的指针是否为空指针.
内置指针(非智能指针)管理的动态内存在被显式释放之前都会一直存在,因此我们需要记得去释放,否则是及其消耗内存的.
使用内置指针通常会犯以下三个错误(因此书中推荐使用智能指针):
忘记delete内存.
使用已被释放的对象.
同一块内存释放两次.
delete之后,指针变成空悬指针,如果需要保留指针,那么最好是在delete之后,将其赋值为nullptr(空指针).
12.1.3 shared_ptr和new结合使用
使用new来初始化智能指针必须使用直接初始化形式:
shared_ptr<int> p1(new int(10)); //正确,使用直接初始化形式
shared_ptr<int> p2=new int(10); //错误,new返回的是int*而不是shared_ptr
可以使用new,但是还是推荐使用shared_ptr.
并且非常不推荐混用普通指针和智能指针.
12.1.4智能指针和异常
智能指针的异常通常出现在get函数身上,p.get()会返回p保存的指针,如果智能指针p释放的其对象,那么get函数返回的指针指向的对象也会消失,因此只有确定代码不会delete指针的情况下才能使用get,并且不要用get初始化另一个智能指针或是给智能指针赋值.
为了减少异常,我们需坚持以下几条基本规范:
12.1.5 unique_ptr
使用上unique_ptr和shared_ptr差不多.
不同的是unique_ptr一个时刻只能有一个unique_ptr指向一个对象,并且初始化时必须采用直接初始化形式.unique_ptr不支持拷贝或赋值操作.
12.1.6 weak_ptr
weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象.
创建weak_ptr时需要一个shared_ptr来初始化它.
来通过weak_ptr访问对象时需要先用w.lock()来检查一下指向的对象是否存在
12.2动态数组
虽然书中有介绍动态数组的这个章节,但是同时也说了更推荐使用标准库容器.因此不再多介绍,只提炼出几点需要注意的点.
使用动态数组需要牢记动态数组并不是数组类型.
delete释放动态数组需要在delete后加上方括号[].