shared_ptr使用引用计数(use_count),每一个shared_ptr
的拷贝都指向相同的内存。再最后一个
shared_ptr
析 构的时候,内存才会被释放。
shared_ptr实现包含了两部分:
1.shared_ptr的基本用法
1)初始化
通过构造函数、
std::shared_ptr
辅助函数和
reset
方法来初始化
shared_ptr
std::shared_ptr<int> p1(new int(1));
std::shared_ptr<int> p2 = p1;
std::shared_ptr<int> p3;
p3.reset(new int(1)); //构建对象
p3.reset(); //析构对象
注意:
这是因为:make_shared
通常具有更好的性能,因为它可以在一次堆分配中同时分配对象和控制块,而不是两次分配(一个用于对象,一个用于控制块),而普通的shared是进行两次分配,先分配一个内存块用于存储对象,然后再分配另一个内存块用于存储控制块,这就是两次分配。
auto sp1 = make_shared<int>(100) 等价于 shared_ptr<int> sp1(new int(100));
2)获取原始指针
当需要原始指针的时候。可以通过调用get返回luo'zhi
shared_ptr<TEST> p3(p1); //引用计数+1
p3.reset(new TEST); //reset有参数表示分配资源, 无参表示释放资源
TEST *p = p3.get();
注意:
-
不要delete p.get()的返回值 ,会导致对一块内存delete两次的错误
-
不要保存
p.get()
的返回值 ,无论是保存为裸指针还是
shared_ptr
都是错误的
-
保存为裸指针不知什么时候就会变成空悬指针,保存为
shared_ptr
则产生了独立指针
3)指定删除器
当shared_ptr管理非new的对象或者没有析构函数的对象的时候,应该传递合适的删除器
3-1示例表示自动关系析构函数
3-1) 函数做删除器
void release_source(TEST *p)
{
cout<<"release_source"<<endl;
delete p;
}
int main()
{
shared_ptr<TEST> p1(new TEST,release_source);
return 0;
}
3-2) Lambda表达式做删除器
shared_ptr<TEST> p2(new TEST,[](TEST *p){
cout<<"release_source"<<endl;
delete p;});
注意:
- 当智能指针管理数组的时候需要指定删除器或使用模板类default_delete(因为shared_ptr默认删除器不支持数组对象)
原因:智能指针默认的删除器(deleter)是针对单个对象的,而不是数组对象。这是因为C++中数组的内存布局和单个对象是不同的。数组通常需要在内存中连续存储多个元素,而单个对象只占用一个内存块。C++11引入了std::shared_ptr
的一个专门版本:std::shared_ptr<T[]>
,它支持管理数组对象。当你使用std::shared_ptr<T[]>
时,你需要提供一个删除器,该删除器知道如何使用delete[]
来释放内存,从而正确处理数组。
std::shared_ptr<int> p3(new int[10], [](int *p) { delete [] p;});
std::shared_ptr<int>ptr(new int[10],std::default_delete<int[]>());
2.shared_ptr使用问题
1)不能用一个原始指针初始化多个shared_ptr,更不能初始化非堆的内存。
int *p = new int;
shared_ptr<int>ptr1(p);
shared_ptr<int>ptr2(p); //逻辑错误
2)不能在函数实参中创建智能指针
function(shared_ptr<int>(new int), g()); //有缺陷
C++函数参数对于不同的编译器执行过程可能时不同的,可能从左到右或从右到左。假设想new int,然后调用g().此时g发生了异常 ,shared_ptr还没创建完成(用make_shared会快一些),就会造成内存泄漏
正确做法:
shared_ptr<int> p(new int);
function(p, g());
3)不要将this指针作为shared_ptr返回回来。
this指针本质也是一个裸指针,可能会造成重复析构
class TEST
{
public:
TEST()
{
cout<<"new TEST"<<endl;
}
~TEST()
{
cout<<"delete TEST"<<endl;
}
shared_ptr<TEST>get_ptr()
{
return shared_ptr<TEST>(this);
}
};
int main()
{
shared_ptr<TEST>p1(new TEST);
shared_ptr<TEST>p2 = p1->get_ptr();
return 0;
}
![](https://img-blog.csdnimg.cn/c9ee0de9c4d64af9994cc0068ead7890.png)
在这个例子中,由于用同一个指针(this)构造了两个智能指针sp1和sp2,而他们之间是没有任何关系 的,在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误
正确做法:
让目标类通过
std::enable_shared_from_this
类,然后使用基类的
成员函数
shared_from_this()
来返回
this
的
shared_ptr
class TEST:public enable_shared_from_this<TEST>
{
public:
TEST()
{
cout<<"new TEST"<<endl;
}
~TEST()
{
cout<<"delete TEST"<<endl;
}
shared_ptr<TEST>get_ptr()
{
return shared_from_this();
// return shared_ptr<TEST>(this);
}
};
4)循环引用
智能指针shared_ptr的出现可以使得同一资源被多个指针共享,并且保证共享资源只被释放一次,其内部使用计数器原理。但是两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。
看案例:
#include <string.h>
#include <unistd.h>
#include<iostream>
#include <memory>
using namespace std;
class B;
class A
{
public:
shared_ptr<B> pb_;
~A()
{
cout<<"A delete"<<endl;
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
cout<<"B delete"<<endl;
}
};
int main()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
pb->pa_ = pa;
pa->pb_ = pb;
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
return 0;
}
![](https://img-blog.csdnimg.cn/ab98ab784aee4591800f8ea16205239f.png)
解决办法:
下面会讲
weak_ptr对象指向shared_ptr对象时(反之亦然),不会增加shared_ptr中的引用计数。
当然使用weak_ptr的时候需要注意访问对象的方法。
shared_ptr<B> pb_ -> weak_ptr<B> pb_;
二 . unique_ptr(独占智能指针)
独占智能指针不允许其他智能指针共享,也不允许通过将一个
unique_ptr赋值到unique_ptr中。但是unique_ptr可以通过move操作移动unique_ptr(原指针失效) 当然也可以通过返回进行返回。以下举例:
1. 不允许赋值
#include<iostream>
#include<memory>
using namespace std;
class TEST
{
public:
TEST()
{
cout<<"new TEST"<<endl;
}
~TEST()
{
cout<<"delete TEST"<<endl;
}
};
int main()
{
unique_ptr<TEST> p1(new TEST);
unique_ptr<TEST> p2(p1); //错误
return 0;
}
2.通过std::move 移动
unique_ptr<TEST> p1(new TEST);
unique_ptr<TEST> p2(std::move(p1)); //正确
3.使用make_unique效率更高(C++14引入)
auto p3(make_unique<TEST>());
4. unique_ptr 指定删除器
对于shared_ptr,指定删除器
shared_ptr<int>p1(new int, [](int *p){delete p;});
对于unique_ptr,指定删除器需要确定删除器的类型
unique_ptr<int, void(*)(int *)> p6(new int, [](int *p){delete p;});
比如指针是指向一个数组,对于共享指针,默认删除器是不能删除数组的,需要注意。
三 . weak_ptr(弱引用智能指针)
weak_ptr是一种不控制对象生命周期的弱引用指针(因为没有引用计数)。用来解决shared_ptr相互引用导致引用计数不能减为0。它不会增加引用计数,可以和shared_ptr之间相互转化,shared_ptr可以赋值给他(反之亦然),可以通过lock函数获取shared_ptr指针。
weak_ptr没有*和->,因为它不共享指针,构造和析构不会造成引用计数的增减。作为一个纯粹的旁观者监测shared_ptr变化。weak_ptr还可以返回this指针和解决循环引用的问题
1.基本用法
1).weak_ptr使用
shared_ptr<int> s1(new int);
weak_ptr<int> w1(s1);
cout<< w1.use_count()<<endl; //输出1
2). 通过expired()方法判断所观察资源是否已经释放
int main()
{
shared_ptr<int> s1(new int);
weak_ptr<int> w1(s1);
weak_ptr<int> w2(move(w1));
cout<< w1.use_count()<<endl;
if(w1.expired())
cout<<"weak_ptr invalid, Resources have been released"<<endl;
else
cout<<"weak_ptr valid, Resources is in use"<<endl;
return 0;
}
通过move将w1指针移动到w2.w1释放。
![](https://img-blog.csdnimg.cn/d55f48445a77421184299df850b128a5.png)
3). 通过lock方法获取监视的shared_ptr
std::weak_ptr<int> ptr2;
void f()
{
if(ptr2.expired())
{
cout << "ptr2 invaild"<<endl;
}else
{
auto spt = ptr2.lock();
cout << "ptr2, *spt = " << *spt << endl;
}
}
int main()
{
{
auto ptr1 = make_shared<int>(42);
ptr2 = ptr1;
f();
} //作用域结束,指针释放
f();
return 0;
![](https://img-blog.csdnimg.cn/2423cb2d5e584e8aa70d26f6d9eec409.png)
2. weak_ptr返回this指针
在使用shared_ptr的时候是不能直接将this指针返回shared_ptr的,需要通过继承enable_shared_from_this,然后基类调用shared_from_this()返回指针。这是因为
enable_shared_from_this中有一个weak_ptr.这个weak_ptr通过lock观察this指针.将观察的shared_ptr返回。
3. 解决循环引用
智能指针shared_ptr的出现可以使得同一资源被多个指针共享,并且保证共享资源只被释放一次,其内部使用计数器原理。但是两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。
看案例:
#include <string.h>
#include <unistd.h>
#include<iostream>
#include <memory>
using namespace std;
class B;
class A
{
public:
shared_ptr<B> pb_;
~A()
{
cout<<"A delete"<<endl;
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
cout<<"B delete"<<endl;
}
};
int main()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
pb->pa_ = pa;
pa->pb_ = pb;
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
return 0;
}
![](https://img-blog.csdnimg.cn/ab98ab784aee4591800f8ea16205239f.png)
解决办法:
weak_ptr对象指向shared_ptr对象时(反之亦然),不会增加shared_ptr中的引用计数。
当然使用weak_ptr的时候需要注意访问对象的方法。
shared_ptr<B> pb_ -> weak_ptr<B> pb_;
weak_ptr使用注意事项:
在使用wp前需要调用wp.expired()函数判断一下。避免对象已经释放问题
四:智能指针使用安全问题
- 注意多线程共享智能指针问题,后续补充