shared_ptr
shared_ptr允许有多个指针指向同一个对象。
每个share_ptr都有一个关联的计数器,通常称为引用计数(reference count)。拷贝一个shared_ptr,计数器递增。
当指向的最后一个shared_ptr被销毁时,是调用析构函数来完成销毁工作的,析构函数会先递减它所指向的对象的引用计数,再检查引用计数值,如果引用计数变为0,那么shared_ptr析构函数就会销毁对象,并释放它占用的内存。
初始化
int *p = new int(30);
std::shared_ptr<int> a(p);//方式1
auto b = std::make_shared<int>(30);//方式2 std::make_shared 可以返回一个指定类型的std::shared_ptr,优点在于减少了内存分配的次数,效率更高
std::shared_ptr<int> c(b);//方式3
指定删除器
当指针引用技术为0时,自动调用指定的删除器来释放内存
void DeleteIntPtr(int* p) {
cout << "delete" << endl;
delete p;
}
std::shared_ptr<int> p(new int(33), DeleteIntPtr);
//删除器可以是lambda表达式
std::shared_ptr<int> p(new int(33), [](int* p){delete p;});
p.reset();//使p原指针引用次数-1 out: delete
用shared_ptr管理动态数组时,必须指定删除器,因为shared_ptr的默认删除器不支持数组对象
std::shared_ptr<int> p(new int[10], [](int* p){ delete[] p; });
//可以将std::default_delete作为删除器,其内部调用delete
std::shared_ptr<int> p(new int[10], std::default_delete<int[]>);
//封装个方法
template<typename T>
shared_ptr<T> make_shared_array(size_t size){
return shared_ptr<T>(new T[size], std::default_delete<T[]>() );
}
auto p = make_shared_array<int>(10);//使用
成员函数
use_count:返回引用计数的个数
unique:返回是否是独占所有权( 是否use_count 为 1)
swap:交换两个 shared_ptr 对象(即交换所拥有的对象)
reset:放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数减1
get:返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的.如 shared_ptr sp(new int(1)); 其中sp 与 sp.get()是等价的
//use_count, reset, unique
std::shared_ptr<int> sp3(new int(22));
std::shared_ptr<int> sp4 = sp3;
printf("%d %d\n", sp3.use_count(), sp3.unique()); // 2 0
printf("%d %d\n", sp4.use_count(), sp4.unique()); // 2 0
sp3.reset();
printf("%d %d\n", sp3.use_count(), sp3.unique()); // 0 0
printf("%d %d\n", sp4.use_count(), sp4.unique()); // 1 1
//swap
printf("%d %d\n", *r, *sp4); //11 22
r.swap(sp4);
printf("%d %d\n", *r, *sp4); //22 11
注意
- 禁止纯指针给智能指针赋值或者拷贝构造
int* a = new int(2); share_ptr<int> sp = a;//error
- shared_ptr多次引用同一数据,会导致两次释放同一内存,所以无法用一个指针初始化多个shared_ptr
int* pInt = new int[100]; shared_ptr<int> sp1(pInt); //..... shared_ptr<int> sp2(pInt);//error
- 使用shared_ptr包装this指针带来的问题,容易导致多次释放对象(出栈和智能指针析构)。解决方法:继承enable_shared_from_this并正确使用sp指针。
class a: public std::enable_shared_from_this<a>{ shared_ptr<a> GetSelf(){ return shared_ptr<a>(this);//error return shared_from_this();//accept, 返回this的shared_ptr } }
- shared_ptr循环引用导致内存泄露(两个类里面的智能指针互指,计数器永远无法变0释放)。引入weak_ptr可以解决这个问题
//如下所示 struct a; struct b; struct a{ std::shared_ptr<b>bptr;}; struct b{ std::shared_ptr<a>aptr;}; void test(){ std::shared_ptr<a> ap(new a); std::shared_ptr<b> bp(new b); ap->bptr = bp; bp->aptr = ap; }
- 在多线程程序中使用shared_ptr
shared_ptr内部的引用计数是线程安全的,但是其操作维护的对象不是线程安全的。
unique_ptr
unique_ptr “唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。
unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。
std::unique_ptr<int> uptr(new int(10));
//std::unique_ptr<int> uptr2 = uptr; //error 不能赋值
//std::unique_ptr<int> uptr2(uptr); //error 不能拷贝
std::unique_ptr<int> uptr2 = std::move(uptr); //转换所有权
uptr2.release(); //释放
unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。
unique_ptr<int> clone(int p){
unique_ptr<int> pInt(new int(p));
return pInt; // 返回unique_ptr
}
unique_ptr<int> ret = clone(5);
指定删除器的时候需要指定删除器类型
std::unique<int, void(*)(int*)> ptr(new int, [](int* p) {delete p;});
std::shared_ptr<int> p(new int(33), [](int* p){delete p;});//和shared_ptr对比一下
__在多线程程序中使用unique_ptr
对于unique_ptr,由于只是在当前代码块范围内生效,因此不涉及线程安全问题。
使用场景
- 为动态申请的资源提供异常安全保证.
使用unique_ptr来管理动态内存,只要unique_ptr指针创建成功,其析构函数最终都会被调用。确保动态资源被释放。 - 返回函数内动态申请资源的所有权
上面说的从函数中返回一个unique_ptr。 - 在容器中保存指针
vector<unique_ptr<int>> vec; unique_ptr<int> p(new int(5)); vec.push_back(std::move(p)); // 使用移动语义
- 管理动态数组
unique_ptr<int[]> p(new int[5] {1, 2, 3, 4, 5}); p[0] = 0; // 重载了operator[]
- 作为auto_ptr的替代品
weak_ptr
weak_ptr更像是shared_ptr的助手:
0、创建和销毁不会导致其引用的shared_ptr的计数器加减。
1、他不像其余三种,可以通过构造函数直接分配对象内存;他必须通过shared_ptr来共享内存。
2、没有重载opreator*和->操作符,也就意味着即使分配到对象,他也没法使用该对象
3、不主动参与引用计数,即,share_ptr释放了,那么weak_ptr所存的对象也释放了。
4、use_count()可以查看当前引用计数
5、expired()判断引用计数是否为空。
6、lock()函数,返回其引用的shared_ptr智能指针
成员函数
weak_ptr<int> wp;
void f() {
if (wp.expired()) {//已被释放
cout << "expired" << endl;
}else {
auto spt = wp.lock();
cout << *spt << endl;
}
}
int main(){
{
shared_ptr<int> sp(new int(10));
wp = sp;
f();//10
cout << wp.use_count() << endl; //1
}
f();//expired
cout << wp.use_count() << endl; //0
}
解决返回this指针
前面shared_ptr用过。
因为tihs指针不能直接返回为shared_ptr…所以enable_shared_from_this中有一个weak_ptr,用来观测this智能指针,调用shared_from_this其实就是调用weak_ptr的lock(),会返回其观测的shared_ptr
class a: public std::enable_shared_from_this<a>{
shared_ptr<a> GetSelf(){
return shared_ptr<a>(this);//error
return shared_from_this();//accept, 返回this的shared_ptr
}
}
解决循环引用
只需将上述的情况其中一个改为weak_ptr
struct a;
struct b;
struct a{ std::shared_ptr<b>bptr;};
struct b{ std::weak_ptr<a>aptr;};
void test(){
std::shared_ptr<a> ap(new a);
std::shared_ptr<b> bp(new b);
ap->bptr = bp;
bp->aptr = ap;
}
//上面的操作,ap的引用计数是1.
//离开作用域后,ap的引用计数器会减为0,a被析构,bptr为1
//离开作用域后bp也减为1,b被析构。