全局对象:启动时分配,结束时销毁
局部对象:程序块内分配,程序块外销毁
static对象:第一次使用分配,结束时销毁
动态内存使用new来分配对象,使用delete销毁对象
12.1两种智能指针
#include<memory>
shared_ptr: 多个指针可以指向同一个对象
unique_ptr: 独占指向的对象
weak_ptr: 一个伴随类,指向shared_ptr管理的对象,是弱引用
智能指针也是模板,定义时需要指明类型
shared_ptr<string> p1;
shared_ptr<list<int>> p2;
默认初始化为空指针
shared_ptr和unique_ptr都支持的操作 |
|
unique_ptr<T> up shared_ptr<T> sp |
空智能指针,指向类型为T的对象 |
p |
作为一个条件判断,指向一个对象则为true |
*p |
解引用,获得指向的对象 |
p->item |
等价于(*p).item |
p.get() |
返回p中保存的指针 |
swap(p, q) p.swap(q) |
交换p和q中的指针 |
shared_ptr独有的操作 |
|
make_shared<T>(args) |
返回一个shared_ptr,使用args初始化T的对象 |
shared_ptr<T> p(q) |
p是q的拷贝,指向同一个对象,q中计数器递增 |
p=q |
递减p中引用计数,递增q中引用计数 |
p.unique() |
如果p.use_count()为1,返回true |
p.use_count() |
返回p共享对象的智能指针的数量,很慢,用于调试 |
使用动态内存的三种原因
- 程序不知道需要使用多少对象
- 程序不知道所需对象的准确类型
- 程序需要在多个对象间共享数据
12.1.2直接管理内存
int *ptr = new int; //默认初始化,值未定义 int *ptr1 = new int();//值初始化为0 int *ptr2 = new int(1024); const int *ptr3 = new const int(1024); int *ptr4 = new (nothrow) int(1024);//分配失败,不抛出bad_alloc异常,而是返回空指针 string *str = new string; //默认初始化为空string string *str1 = new string();//值初始化为空string string *str2 = new string("string"); vector<int> *pv = new vector<int>{ 1, 2, 3, 4, 5 }; auto p = new auto("123");//为const char** auto p = new auto(string("123"));//为string* int i, *pi1 = &i, *pi2 = nullptr; double *pd = new double(33), *pd2 = pd; delete i; // 错误,i不是指针 delete pi1; // 未定义,pil是一个局部变量 delete pd; // 成功 delete pd2; // 未定义,pd2已经被释放 delete pi2; // 成功,释放了一个为空的指针
释放内存之后,指针变成了悬空指针(dangling pointer),通常仍然指向原来的内存地址,如果之后再次delete,会破坏自由空间内存,通常需要重新赋予nullptr。
12.1.3结合使用shared_ptr和new
shared_ptr<int> p2 = new int(42);// 错误int*不能隐式转换成智能指针 shared_ptr<int> p2(new int(42));// 正确,因为构造函数是explicit,上边才不成立 int* q = new int(10); shared_ptr<int> p(q, [](int* p) {cout << *p; delete p; });// 自定义释放内存的方法
不建议结合使用
其他操作
shared_ptr<int> p(new int(9)); int *pi = p.get();// 得到内置指针 if (!p.unique())//不是唯一引用 p.reset(new int(*p + 1));//改变p的指向,不影响其他引用的值 *p = *p + 1;//唯一引用,直接改变也不影响其他的了
12.1.4智能指针与.Net using
void f(destination &d) { connection c = connect(&d); //保证在程序结束后释放c shared_ptr<connection> p(&c, [](auto c) {delete c; }); ... }
12.1.5独占智能指针unique_ptr
unique_ptr不能进行赋值、拷贝操作
unique_ptr<int> u; u = nullptr;//释放u的对象,并置空 u.release();//释放u的对象控制,并返回内置指针,置空u u.reset();//释放u的对象 u.reset(q);//指向q,释放u的对象
可以拷贝和返回一个将要被销毁的unique_ptr,例如参数返回时的拷贝,这其实是一种特殊的拷贝(13.6.2)。
自定义删除器需要在定义时指明类型,这与shared_ptr作为参数不同。
unique_ptr<int, decltype(process)*> u(new int(10), process);
12.1.6弱智能指针weak_ptr
weak_ptr将会绑定到一个shared_ptr,它不会改变shared_ptr的引用计数。
弱智能指针需要用shared_ptr对象初始化。
shared_ptr<int> u1(new int(50)); weak_ptr<int> w = u1; w.reset();// 置空w w.use_count();//共享shared_ptr的数量 w.expired();//user_count为0,返回true,否则为false w.lock();//返回对应的一个shared_ptr对象,如果expired为true,则返回的是空的对象 if (shared_ptr<int> u = w.lock())//可以判断并得到shared_ptr
12.2动态数组
最好使用vector、string等其他标准库中的容器,这些标准库可以使用默认版本的拷贝、赋值和析构操作,而使用动态数组,就需要自己考虑了。
12.2.1数组的new
int *pia = new int[10];//10个未初始化 int *pia1 = new int[10]();//10个值初始化为0 int *pia2 = new int[10]{ 1,2,3 };//前三个为1,2,3,其他的为0
- 需要注意的是,分配的内存空间并不是数组类型空间,而仅仅是内存空间,并且返回一个首地址。
- 使用new分配的对象,执行默认初始化。
释放时需要用到
delete[] pia;
对于释放时delete中如何知道内存中的大小,一般的编译器是通过在分配的动态数组前记录分配的内存的大小,然后释放的时候读取记录进行释放。
使用智能指针管理动态数组
unique_ptr<int[]> u(new int[10]); cout << u[10] << endl; u.release();//可以自动调用delete [] shared_ptr<int> sp(new int[10], [](int*p) {delete[] p; });//需要自定义销毁函数 sp.reset();//使用自定义销毁函数
shared_ptr不直接支持动态数组管理,所以也不支持下标运算和指针的算术运算,需要使用时,必须使用get获取内置指针。
allocator<T> a |
定义一个为T类型对象分配内存的allocator对象 |
a.allocate(n) |
为n个T类型对象分配内存 |
a.deallocate(p, n) |
收回为n个T类型对象分配的内存 |
a.construct(p, args) |
在p指向的位置构造T对象(需要一个一个构造) |
a.destroy(p) |
销毁p指向位置的T对象(需要一个一个销毁) |
int count = 10; allocator<string> a; //分配内存 string* p = a.allocate(count); for (int i = 0; i < count; i++) a.construct(p + i);//构造对象 for (int i = 0; i < count; i++) a.destroy(p + i);//销毁对象 //收回内存 a.deallocate(p, count);
uninitialized_copy(b,e,b2) |
从迭代器b到e的范围中的对象,内拷贝到b2内存中 |
uninitialized_copy_n(b,n,b2) |
从迭代器b开始的n个元素拷贝到b2内存中 |
uninitialized_fill(b,e,t) |
在b到e的范围中创建t的拷贝 |
uninitialized_fill_n(b,n,t) |
在b开始的n个元素内存中,创建t的拷贝 |