目录
1. 共享指针的作用
共享所管理的指针的内容的传播和释放,例如解决多线程使用同一个对象时的析构问题
使用场景示例:
对于同一帧视频数据
业务A要将数据保存在本地
业务B要将数据上传到云端
这时候如果将数据都深拷贝一份再传入各个业务线程,就会带来很大的资源浪费,此时就可以使用共享指针来管理指向这些数据的指针;
前提是要管理的这些数据,是只读的,如果要修改内容,就要考虑采用加锁等手段来避免产生数据安全的问题
2. 共享指针的声明
make_shared() 声明共享指针
reset() 重置共享指针
// 最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数
// 不用make_shared(),会单独为被管理对象和引用计数各分配一次内存
// 使用make_shared(),会一次分配一块足够大的内存
std::shared_ptr<int> sptr1 = std::make_shared<int>(10);
std::shared_ptr<int> sptr2(new int(20));
// 注意事项
// 不要使用一个裸指针初始化多个shared_ptr,会导致该指针被删除多次
int *ptr = new int(10); // 错误写法
std::shared_ptr<int> sptr(ptr);
std::shared_ptr<int> sptr1(ptr);
get() 获取原始指针
std::shared_ptr<int> sptr = std::make_shared<int>(10);
int *ptr = sptr.get();
// 注意事项
// get() 函数返回共享指针中管理的指针,但是要小心使用
delete ptr; // 会导致双重析构
// 如果共享指针的引用计数=0,释放了其管理的对象,此处的ptr就变成了空悬指针
use_count() 返回共享指针的引用计数
unique() 若use_count() == 1 返回true,否则返回false;
3. shared_from_this()
返回由shared_ptr管理的对象的this指针
使用场景:
一个类中,可能会有业务需求,需要定义一些方法来返回当前对象的this指针,如果直接返回一个this裸指针,而该类又被share_ptr所管理,this裸指针和shared_ptr其实是引用的同一个对象的内存,但是shared_ptr感知不到所返回的this裸指针,就会出现跟使用get()一样的问题,双重析构或空悬指针;
解决方案:shared_from_this() 和继承enable_shared_from_this类,搭配使用(固定写法)
#include<memory>
// 错误写法
class A {
public:
std::shared_ptr<A> GetSelf() {
return std::shared_ptr<A>(this);
}
};
// 正确写法
class A :public std::enable_shared_from_this<A>{
public:
std::shared_ptr<A> GetSelf() {
return shared_from_this();
}
};
int main(){
std::shared_ptr<A> sp1(new A);
std::shared_ptr<A> sp2 = sp1 -> GetSelf();
}
4. 循环引用示例
两个共享指针循环引用,会导致这两个指针的引用计数永远都不会降到0,其管理的对象的析构函数永远都不会被调用,占用的内存就不会被释放,可能导致内存不足、程序崩溃、内存资源浪费、在资源受限的环境中,程序性能下降;
解决方案:使用weak_ptr替代其中一个类中的shared_ptr,因为weak_ptr不会增加对象的引用计数,因此可以防止循环引用
#include <iostream>
#include <memory>
class B;
class A {
public:
std::shared_ptr<B> bsp;
~A() { std::cout << "A destructor" << std::endl; }
};
class B {
public:
std::shared_ptr<A> asp;
~B() { std::cout << "B destructor" << std::endl; }
};
int main() {
std::shared_ptr<A> asp = std::make_shared<A>();
std::shared_ptr<B> bsp = std::make_shared<B>();
asp->bsp = bsp;
bsp->asp = asp;
return 0;
}