12.1 智能指针
智能指针行为类似普通指针,但它负责自动释放所知的对象。
#include <memory>
shared_ptr : 允许多个指针指向同一个对象,每个指针都会记录有多少个其他指针指向相同的对象
unique_ptr : 某个对象只允许一个指针指向它
weak_ptr : 弱引用的伴随类,指向shared_ptr所管理的对象。
shared_ptr 和 unique_ptr支持的操作
shared_ptr<T> sp;//空指针,可以指向类型为T的对象
unique_ptr<T> up;
if (p){}//若p指向一个对象,条件为true
*p;//解引用p,获取其指向的对象
p->mem;//等价于(*p).mem
p.get();//返回p中保存的指针,与对象的引用计数无关,不要使用它来初始化一个智能指针或赋值
swap(p,q);//交换p,q中的指针
p.swap(q);
shared_ptr独有的操作
make_shared<T>(args);//返回一个shared_ptr,指向一个动态分配的类型为T的对象,使用args初始化此对象
shared_ptr<T> p(q);//p是q的拷贝;递增q中的引用计数,q中的指针必须可转换为T*
p = q;//pq保存的指针需可以互相转化,递减p的引用计数,递增q的引用计数;当p的引用计数为0,会将管理的原内存释放
p.use_count();//返回与p共享的对象的智能指针个数。
p.unique();//若p.use_count()返回1,则返回true。
make_shared函数会在动态内存中分配一个对象并初始化。
传递的参数必须符合类型的某个构造函数;内置类型若不提供参数,将进行值初始化。
shared_ptr的拷贝auto p(q);
拷贝操作将会递增引用计数的值:
1,用一个shared_ptr初始化另一个shared_ptr
2,将其作为参数传递给一个函数
3,作为函数的返回值
shared_ptr的赋值和销毁,r = q
1,赋值
2,shared_ptr被销毁
以上操作会递减指针引用计数的值,当计数值为0时释放所管理的对象(shared_ptr的析构函数)
12.1.2 直接管理内存
int *pi = new int;//内置类型和组合类型的对象的值未定义,默认初始化
string *ps = new string;
int *pi = new int(1024);//直接初始化
string *ps = new string(10,'9');
vector<int> *p = new vector<int>{0,1,2,3,4,5,6,7,8,9};
int *pi = new int();//值初始化
string *ps = new string();
初始化器
auto p1= new auto(obj);//根据obj对象推断类型,并用obj初始化,obj只可以拥有一个
动态分配const对象
const int *p=new const int(1024);//需初始化
const string *p = new const string;
int *p=new int;//分配失败,抛出std::bad_alloc
int *p=new (nothrow) int;//分配失败,返回空指针
delete p;//销毁给定指针指向的对象,释放对应的内存
p=nullptr;//重置指针,避免成为空悬指针
释放非new分配的内存或多次释放行为未定义。
12.1.3 shared_ptr和new结合使用
shared_ptr<int> p(new int(42));//接受指针参数的智能指针的构造函数是explicit的,不可将一个内置指针隐式转换为智能指针,必须进行直接初始化
默认情况下,初始化智能指针的普通指针必须指向动态分配的内存(使用delete释放),但可以提供其他代替delete的操作来将智能指针绑定到指向其他类型的指针上。
shared_ptr<T> p(q);//p管理内置指针q(new分配)所指的对象,q可转换为T*
shared_ptr<T>p(u);//p从unique_ptr接管对象,u置空
shared_ptr<T>p(q, d);//p结构内置指针q指向的对象,使用可调用对象d来代替delete
shared_ptr<T>p(p1,d);//p是p1(shared_ptr)的拷贝,但使用d代替delete
p.reset();
p.reset(q);
p.reset(q,d);
若p是唯一指向其对象的shared_ptr,reset会释放此对象,将p置空;若传递了内置指针q,则令p指向q;若还有参数d,会调用d来释放q;会更新引用计数。
共享对象的智能指针的处理:
if (!p.unique()){
p.reset(new string(*p));//p不是唯一指向对象的指针,但此时要改变p指向的元素,必须创建一个拷贝
}
//对p的对象进行操作
12.1.4 智能指针和异常
1,不使用相同的内置指针初始化(或reset)多个智能指针
2,不delete get()返回的指针
3,不使用get() 初始化或reset另一个智能指针
4,使用get()返回的指针,当其对应的最后一个智能指针销毁后,get()返回的指针无效了
5,使用智能指针管理不是有new分配的内存,需要有附加的删除器
12.1.5 unique_ptr
某个时刻只有一个unique_ptr指向一个对象,unique_ptr被销毁时,对象也被销毁。
定义的同时必须初始化,必须采用直接初始化。不支持拷贝和赋值
unique_ptr<int> p4;
unique_ptr<int> p1(new int(1024));
int *p2=new int(1203);
unique_ptr<int> p3(p2);
unique_ptr特有的操作
unique_ptr<T> u1;//
unique_ptr<T, D> u2;//u2使用类型为D的可调用对象释放指针
unique_ptr<T, D> u(d);//使用类型为D的对象代替delete
unique_ptr<T, D> u(p, d);//使用普通指针p初始化u,销毁时使用D类型的对象代替delete
u = nullptr;//释放u所指的对象,将u置空
u.release();//u放弃对指针的控制权,返回指针,并将u置空;可用来初始化另一个智能指针或赋值
u.reset();//释放u所指的对象
u.reset(q);//释放u所指的对象,u指向这个内置指针绑定的对象
u.reset(nullptr);//释放u所指的对象,将u置空;
release和reset可以将对象的所有权转移到另一个unique_ptr上。
unique_ptr<string> p(p1.release());//p1置空,p管理p1的对象
unique_ptr<string> p2(new string("haha"));
p.reset(p2.release());//p释放了原指向的内存,重新指向了p2的内存,p2为空
可以拷贝或赋值一个将要被销毁的unique_ptr,比如从函数返回一个unique_ptr或返回局部unique_ptr的拷贝;
12.1.6 weak_ptr
和某些shared_ptr共享同一个对象,但不会增加shared_ptr的引用计数。weak_ptr不会控制对象的生存期。
weak_ptr<T> w;
weak_ptr<T> w(sp);//w和sp(一个shared_ptr)指向相同的对象,T必须是可以转换为sp指向的类型。
w = p;//p可以是shared_ptr或weak_ptr,赋值后w,p共享对象
w.reset();//将w置为空
w.use_count();//与w共享对象的shared_ptr数量
w.expired();//若w.use_count()为0,则为true
w.lock();//若w.expired()返回true,则返回空的shared_ptr;否则返回一个w指向对象的shared_ptr
不可用weak_ptr直接返回对象,必须用lock;
auto p = make_shared<int>(100);
weak_ptr<int> wp(p);
if (shared_ptr<int> np = wp.lock()){
//np和p共享同一个对象
}
12.2 动态数组
使用new T[] 和delete[]
T *p = new T[n];//n必须是整型,不必是常量。n可以为0,返回合法的非空指针。
typedef int arrInt[100];
int *p = new arrInt;
注意p是元素的指针而不是数组的指针,并且严格说动态数组非数组,只是一段有类型的连续的存。
默认情况下,创建的动态数组执行默认初始化;
int *p1 = new int[10];//值未定义,默认初始化
int *p2 = new int[10]();//值初始化,0
直接初始化
int *p3 = new int[10]{1,2,3,4,5,6,7,8};
delete [] p1;//添加[]和去掉[],需要看p1是单个元素的指针还是动态数组的指针,否则delete操作未定义
可直接使用unique_ptr管理动态数组
unique_ptr<int[]> up(new int[10]);
up.release();//自动调用delete[]销毁
指向数组的unique_ptr不支持成员访问运算符。
unique_ptr<T[]> u;
unique_ptr<T[]> u(p);//内置指针p指向动态数组,p必须可以转换为类型T*
up[i];//需使用下标来访问
shared_ptr不支持直接管理动态数组,但可以定义自己的删除器,间接管理。
shared_ptr<int> sp(new int[10], [] (int *p){ delete [] p;});
sp.reset();//使用定义时的lambda作为删除器
sp.get();//借用此指针访问动态数组的值
12.2.2 allocator 类
#include<memory>
allocator类将内存分配和对象构造分离出来,类型感知的内存分配方法,分配的内存是原始的,未分配的。
allocator<T> a;//a可以为类型为T的对象分配内存
auto p = a.allocator(n);//分配n个未经构造的内存,保存n个类型为T的对象
a.construct(p, args);//p是一个类型为T*的指针,指向一块原始内存;args被传递给类型为T的构造函数,args为参数列表
a.destory(p);//p为类型为T*的指针,对p所指对象执行析构函数,必须是构造过得
a.deallocator(p,n);//p是由allocator返回的指针,n是创建时的大小;释放从p开始的n个类型为T的对象,在此之前必须为每个内存调用destory;
拷贝和填充未初始化内存
uninitialized_copy(b,e,b2);//将范围[b,e)的元素拷贝到b2指向的未构造的内存
uninitialized_copy_n(b,n,b2);//
uninitialized_fill(b,e,t);//在[b,e)指向的原始内存开始创建n个对象,对象的值均为t的拷贝
uninitialized_fill_n(b,n,t);
返回指向最后一个构造的元素的下一位置。