静态内存(局部static对象,类static成员,全局变量)、栈内存(函数内非static变量)、堆(也叫自由空间,存储动态分配的对象,即运行时分配)。动态对象生存期由程序来控制,其他对象由编译器自动创建和销毁。
12.1.1 shared_ptr类
- 程序使用动态内存处于一下三种原因之一:
- 程序不知道自己需要使用多少对象
- 程序不知道所需对象的准确类型
- 程序需要在多个对象间共享数据
12.1.2 直接管理内存
string* ps=new string;//默认初始化
string* ps=new string();//值初始化
vector<int> *p=new vector<int>{1,2,3};//列表初始化
- 对于内置类型,默认初始化和值初始化差别很大:默认初始化的对象的值是未定义的
auto p1=new auto(obj);
- const int* pci=new const int(1024);//一个动态分配的const对象必须进行初始化
- 定位new:
int *p2=new (nothrow) int;//如果分配失败,new返回一个空指针。
int *p1=new int;//分配失败抛出bad_alloc
delete p(指针);
做两件事:销毁给定的指针指向的对象;释放对应的内存- 传递给delete的指针必须指向动态分配的内存,或是一个空指针
12.1.3 shared_ptr和new的结合使用
- 接受指针参数的只能指针的构造函数是explicit的,必须使用直接初始化形式来初始化一个只能指针
shared_ptr<int> p1(new int(1024));
- 我们可以将只能指针绑定到一个指向其他类型资源的指针上,但是为了这样做,必须提供自己的操作来代替delete
- 两个智能指针初始化为同一个new 得到的指针,则这两个只能指针是独立的。这么做十分危险
智能指针与异常
- 智能指针被销毁时会检查引用计数,若引用计数变为0会对他所管理的指针进行delete操作。我们可以定义自己的删除器代替delete
智能指针使用规范:
- 不要使用相同的内置指针初始化或reset多个只能指针(会导致多次free)
- 不要delete get()返回的指针
- 不适用get()初始化或reset另一个智能指针
- 如果你使用只能指针管理的资源不是new分配的内存,记住传递给它一个删除器
12.1.5 unique_ptr
- unique_ptr不拥有它指向的对象,因此unique_ptr不支持普通的拷贝和赋值
u.release();//放弃对指针的控制权,返回指针,并将u置为空。但堆内存还在
- u.reset(q);//释放u指向的对象(堆内存释放掉了),另u指向q指向的对象
- 如果我们不用另一个只能指针来保存release返回的指针,我们的程序就负责资源的释放
auto p=p2.release();//正确,但我们必须记得delete(p)
- 不能拷贝unique_ptr的例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr
unique_ptr<T,D> u(d);
重载一个unique_ptr中的删除器会影响到unique_ptr类型以及如何构造该类型的对象(与关联容器比较类似)
12.2.1 new 和数组
int *pia=new int[get_size()];//方括号中的大小必须是整型,但不必是常量
typedef int arrT[42];
int *p=new arrT;由于分配的类型并不是一个数组类型,因此不能对动态数组调用begin和end来返回指向首元素和尾后元素的指针。也不能用范围for语句来处理所谓的动态数组中的元素。要记住我们所说的动态数组并不是数组类型,这是重要的。
int *pia=new int[10];//默认初始化
int *pia=new int[10]();//值初始化
int *pia=new int[3]{1,2};
char *p=new char[0];//动态分配一个空数组是合法的,但p不能解引用。此时new返回一个合法的非空指针(类似于尾后指针、尾后迭代器),可以用于比较。
delete [] p;//销毁p指向数组中的元素,并释放对应的内存,逆序销毁:最后一个最先销毁
unique_ptr<int[]> up(new int[10]);
- shared_ptr不直接支持管理动态数组,如果希望其管理动态数组,必须提供自己定义的删除器:
shared_ptr<int> sp(new int[10],[](int *p){delete[]p;});
- shared_ptr默认使用delete销毁他指向的对象,若不定义自己的删除器,10中会现delete一个动态数组的问题(应该用delete[])
12.2.2 allocator类
1. new灵活上有局限,它将内存分配和对象构造组合在了一起,类似的,delete将对象析构和内存释放组合在了一起。
2. allocator类将内存分配和对象构造分离开来。它分配的内存是原始的,未构造的。
3. allocator<string> alloc;
auto const p=alloc.allocate(n);//分配n个未初始化的string
a.construce(p,args);//类似于make_shared函数的参数,这些额外参数必须是与构造的对象的类型相匹配合法的初始化器(args被传递给类型为T的构造函数)
- 为了使用allocate返回的内存,我们必须使用construct构造对象。使用未构造的对象是未定义行为。
a.destroy(p);//执行p所指对象的析构函数销毁对象。
先销毁每个对象,再释放内存- 我们只能对真正构造了的元素进行destroy操作
- 元素被销毁后,可以重新使用这部分内存,也可以将齐归还给系统
a.deallocate(p,n);