智能指针
unique_ptr
unique_ptr
独享它指向的对象,也就是说,同时只有一个 unique_ptr
指向同一个对象,当这个unique_ptr
被销毁时,指向的对象也随即被销毁。
简单理解:智能指针就是一个类,成员就是其管理的普通的指针,当智能指针销毁时,会自动调用其析构函数,其析构函数会delete释放普通指针。
1)初始化
方法一:
unique_ptr<AA> p0(new AA("西施"));//分配内存并初始化。
方法二:
unique_ptr<AA>p0= make_unique<AA>("西施");//C++14 标准。
方法三:
AA* p= new AA("西施");
unique_ptr<AA> p0(p);//用已存在的地址初始化。
2)使用方法
智能指针重载了 * 和->操作符,可以像使用指针一样使用 unique_ptr
。
-
不支持普通的拷贝和赋值。
AA* p= new AA("西施"); unique_ptr<AA> pu2 = p;//错误,不能把普通指针直接赋给智能指针 unique_ptr<AA> pu3 = new AA("西施");//错误,不能把普通指针直接赋给智能指针 unique_ptr<AA> pu2 = pu1;//错误,不能用其它 unique_ptr拷贝构造。 unique_ptr<AA> pu3;< pu3 = pu1;//错误,不能用=对 unique_ptr进行赋值。 //因为智能指针类中禁用了 拷贝构造函数 和 =赋值
-
不要用同一个裸指针初始化多个
unique_ptr
。 -
get()方法返回裸指针。
-
不要用
unique_ptr
管理不是 new 分配的内存。
3)用于函数的参数
- 传引用(不能传值,因为unique_ptr禁用了拷贝构造函数)。
- 裸指针。
4)不支持指针的运算(+、-、++、-)
5)更多用法
-
将一个
unique_ptr
赋给另一个时,如果源unique_ptr
是一个临时右值,编译器允许这样做;如果源unique_ptr
将存在一段时间,编译器禁止这样做。一般用于函数的返回值。unique_ptr<AA>p0; p0 = unique_ptr<AA>(new AA("西瓜)"); //unique_ptr<AA>(new AA("西瓜)")是匿名对象是一个临时的右值,可以直接复制
-
用 nullptr给
unique_ptr
赋值将释放对象,空的unique_ptr==nullptr。 -
release()释放对原始指针的控制权,将unique_ptr置为空,返回裸指针。(可用于把
unique_ptr
传递给子函数,子函数将负责释放对象。 -
std::move()函数可以转移对原始指针的控制权。(可用于把
unique_ptr
传递给子函数,子函数形参也是unique ptr)。 -
reset()释放对象。
void reset(T*_ptr=(T*) nullptr); pp.reset(); //释放pp对象指向的资源对象。 pp.reset(nullptr); //释放pp对象指向的资源对象 pp.reset(new AA("bbb")); //释放pp指向的资源对象,同时指向新的对象。
-
swap()交换两个
unique_ptr
的控制权。
void swap(unique_ptr<T>&_Right);
-
unique_ptr也可象普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。
-
unique_ptr
不是绝对安全,如果程序中调用 exit()退出,全局的unique_ptr
可以自动释放,但局部的unique_ptr
无法释放。
shared_ptr
1)初始化
方法一:
shared_ptr<AA>p0(new AA("西施"));//分配内存并初始化.
方法二:
shared_ptr<AA>p0= make shared<AA>("西施");//C++11标准,效率更高.
方法三:
AA*p= new AA("西施");
<shared_ptr<AA>p0(p);//用已存在的地址初始化.
方法四:
shared_ptr<AA>p0(new AA("西施")); shared_ptr<AA>p1(p0);//用已存在的shared_ptr初始化,计数加1. shared_ptr<AA>p1=p0;//用已存在的shared_ptr初始化,计数加1.
2)使用方法
智能指针重载了*和->操作符,可以像使用指针一样使用 shared_ptr
- use_count()方法返回引用计数器的值.
- unique()方法,如果use_count()为1,返回true,否则返回false.
- shared_ptr支持赋值,左值的shared_ptr的计数器将减1,右值shared_ptr的计算器将加1.
- get()方法返回裸指针.
- 不要用同一个裸指针初始化多个shared_ptr.
- 不要用 shared_ptr管理不是new分配的内存.
3)用于函数的参数
与unique_ptr
同理。
4)不支持指针的运算(+、-、++、-)
5)更多用法
-
将一个unique_ptr
赋给另一个时,如果源unique_ptr
是一个临时右值,编译器允许这样做;如果源unique_ptr
将存在一段时间,编译器禁止这样做。一般用于函数的返回值。 -
用 nullptr给
shared_ptr
赋值将释放对象,空的shared_ptr==nullptr。 -
release()释放对原始指针的控制权,将unique_ptr置为空,返回裸指针。 -
std::move()函数可以转移对原始指针的控制权。还可以将unique_ptr转换为shared_ptr。
-
reset()释放对象。
pp.reset();//解除与资源的关系,资源的引用计数减1. pp.reset(new AA("bbb"));//解除与资源的关系,资源的引用计数减1.关联新资源.
-
swap()交换两个
shared_ptr
的控制权。
void swap(shared_ptr<T>&_Right);
-
shared_ptr
也可象普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。 -
shared_ptr
不是绝对安全,如果程序中调用 exit()退出,全局的unique_ptr
可以自动释放,但局部的unique_ptr
无法释放。 -
shared_ptr
的线程安全性:shared_ptr
的引用计数本身是线程安全(引用计数是原子操作)。- 多个线程同时读同一个
shared_ptr
对象是线程安全的。 - 如果是多个线程对同一个
shared_ptr
对象进行读和写,则需要加锁。 - 多线程读写
shared_ptr
所指向的同一个对象,不管是相同的shared_ptr
对象,还是不同的shared_ptr
对象,也需要加锁保护。
-
如果
unique_ptr
能解决问题,就不要用shared_ptr
。unique_ptr
的效率更高,占用的资源更少。
weak_ptr
1)shared_ptr存在的问题
shared_ptr
内部维护了一个共享的引用计数器,多个shared_ptr
可以指向同一个资源。
如果出现了循环引用的情况,引用计数永远无法归0,资源不会被释放。
例如有两个类,AA和BB,AA类有一个成员是BB,BB类有一个成员是AA。
AA->m_p = BB;
BB->m_p = AA;
上述代码就导致了循环引用的问题。
2)weak_ptr是什么
weak_ptr
是为了配合 shared_ptr
而引入的,它指向一个由 shared_ptr
管理的资源但不影响资源的生命周期。也就是说,将一个weak_ptr
绑定到一个shared_ptr
不会改变shared_ptr
的引用计数。不论是否有weak_ptr
指向,如果最后一个指向资源的 shared_ptr
被销毁,资源就会被释放。
weak_ptr
更像是 shared_ptr
的助手而不是智能指针。类似于windows系统下的快捷方式或者linux系统下的软链接。
3)使用方法
weak_ptr
没有重载->和*操作符,不能直接访问资源。
有以下成员函数:
operator=();//把 shared_ptr或 weak_ptr赋值给weak_ptr.
expired();//判断它指资源是否已过期(已经被销毁).
lock();//返回 shared_ptr,如果资源已过期,返回空的 shared_ptr.
reset();//将当前weak_ptr指针置为空.
swap();//交换.
4)注意事项
pa是一个shared_prt
,pa所指的对象的成员变量里有一个weak_ptr
类型的成员mp。
if(pa->mp.expired()== true)
cout<<"pa->m_p已过期.\n";
else
cout <<"pa->m_p.1ock()->m_name="<<pa->m_p.1ock()->m_name <<end1;
上述代码在单线程里面是没问题的,但是对于多线程来说,是不安全的。因为判断mp是否过期和mp.lock()
不是原子操作。会有其他线程在这个过程中释放mp。
weak_ptr
不控制对象的生命周期,但是,它知道对象是否还活着。
用lock()
函数把它可以提升为 shared_ptr
,如果对象还活着,返回有效的shared_ptr
,如果对象已经死了,提升会失败,返回一个空的 shared_ptr
。
提升的行为(lock()
)是线程安全的(原子操作)。
shared ptr<BB> pp = pa->m_p.lock(); //把weak_ptr提升为shared_ptr
if(pp == nullptr)
cout<<"pa->m_p已过期.\n";
else
cout <<"pa->m_p.1ock()->m_name="<<pp->m_name <<end1;
智能指针的删除器
在默认情况下,智能指针过期的时候,用delete原始指针;释放它管理的资源。
程序员可以自定义删除器,改变智能指针释放资源的行为。
删除器可以是全局函数、仿函数和 Lambda表达式,形参为原始指针。
以上资料参考链接:https://www.bilibili.com/video/BV1gV4y1G7fH?p=1&vd_source=fc6bd12a18142f9247518e546641c48f