参考博客:
文章目录
以下以 类 Test为例进行说明。
class Test
{
};
1. shared_ptr初始化规则:同一空间不能初始化多个智能指针,共享型智能指针共享的前提是通过已有的指针指针初始化新的智能指针。
注意:shared_ptr 的实现中,通过引用计数来判断对象是否可销毁。而shared_ptr的设计中,构造函数并不会检查一片空间是否已经被其他空间所引用,因此我们在调用shared_ptr的构造函数时需要留心,避免将已存在智能指针引用的空间 构建 新的智能指针。
这里注意区分,一种是合法的产生新智能指针,即通过一个智能指针初始化另一个智能指针,进行次操作后,另个指针共享同一片空间,引用计数为 2 。当两个指针都放弃了对资源的拥有权时,资源的到释放。
比如这里的,spta 初始化 sptb 。
shared_ptr<Test> spta(new Test());
shared_ptr<Test> sptb = spta;
cout << spta.use_count() << " "
<< sptb.use_count() << endl; // 2 2
另一种不合法的产生新智能指针,即用初始过其他智能指针的内存空间初始化新智能指针。此方法产生的两个智能指针之间彼此独立,都认为自己独占资源。而一旦某个智能指针生存周期结束就会释放资源,而另一个正在使用资源的智能指针就会非法的访问内存。如果侥幸另一个智能指针在此后的时间里没有再使用资源,那么在该指针生存周期结束时会“再次”释放资源,这又造成了程序的崩溃。
比如这里的 sptc 与 sptd ,彼此独立,都认为自身独占pt指向的堆空间 。注:以下代码将发生异常。
Test* pt = new Test();
shared_ptr<Test> sptc(pt);
shared_ptr<Test> sptd(pt);
//shared_ptr<Test> sptd(sptc.get());
cout << "是否独占资源 " << boolalpha
<< sptc.unique() << " "
<< sptd.unique() << endl; // true true
cout << "引用计数: " << sptc.use_count()
<< " " << sptd.use_count() << endl; // 1 1
2. 如果我们想在类中对外返回一个自身的智能指针该如何做到呢?
那 Test 类为例,如果我们现在想对外提供一个接口函数 shared_ptr<Test> getPtr();
使其可以返回自身的shared_ptr 指针,如果我们使用这种方式实现,会有什么隐患呢?
class Test
{
public:
shared_ptr<Test> getPtr()
{
return shared_ptr<Test>(this);
}
};
shared_ptr<Test>(this);
该语句实际上调用了shared_ptr的构造函数(类名加括号 == 构造函数的调用),该语句使用 Test 类型的对象生成了一个智能指针,通过接口函数getptr()
返回。
但是此函数方法是存在问题的,当我们使用这样的方式使用时,程序不会产生问题。
Test* t = new Test();
auto spt = t->getPtr();
但这种原始指针与智能指针混用的方式并不好。一般而言,为了防止忘记释放资源我们会使用指针指针的方式管理动态资源。但这种方式就会产生异常。
shared_ptr<Test> spa(new Test());
auto spt = spa->getPtr();
这里实际上就是犯了“使用同一片内存空间初始化不同的智能指针”的错误。
可以看到使用这种方式,不仅无法为我们提供便利,反而成为一种累赘,生怕一不小心就弹出个异常来。而为了更好的完成这个任务(提供对外接口返回自身的共享指针类型),我们引入了模板类shared_from_this()。
3. 引入shared_from_this()类,更安全的生成额外的shared_ptr 实例
先简单的就、说明以下 enable_shared_from_this
类 。此类为一个模板类,定义于头文件<memory>中。
该模板类主要提供了有这几个函数供我们使用
_NODISCARD shared_ptr<_Ty> shared_from_this() { // return shared_ptr
return shared_ptr<_Ty>(_Wptr);
}
_NODISCARD shared_ptr<const _Ty> shared_from_this() const { // return shared_ptr
return shared_ptr<const _Ty>(_Wptr);
}
_NODISCARD weak_ptr<_Ty> weak_from_this() noexcept { // return weak_ptr
return _Wptr;
}
_NODISCARD weak_ptr<const _Ty> weak_from_this() const noexcept { // return weak_ptr
return _Wptr;
}
从 enable_shared_from_this 派生的对象可以在成员函数中使用 shared_from_this 方法来创建实例的 shared_ptr 所有者,这些所有者与现有 shared_ptr 所有者共享所有权。 否则,如果使用创建一个新 shared_ptr 的 this ,它与现有 shared_ptr 所有者不同,后者可能导致无效引用或导致对象被删除多次
因此,我们可以让Test类继承 enable_shared_from_this 类,从而可以更安全的创建共享指针实例。
class Test : public enable_shared_from_this<Test>
{
public:
shared_ptr<Test> getPtr()
{
return shared_ptr<Test>(this);
}
shared_ptr<Test> getShared_ptr()
{
return shared_from_this();
}
};
通过调用 getShared_ptr()
函数生成的实例,他们与其他shared_ptr智能指针共享资源,这样就不会引发共享智能指针各自为营,资源独占的情况了。
shared_ptr<Test> spt(new Test());
cout << "当前资源被引用数: " << spt.use_count() << endl; // 1
shared_ptr<Test> sptb = spt->getShared_ptr();
cout << "当前资源被引用数: " << spt.use_count() << endl; // 2
4. shared_from_this的作用是?使用场景:对象保活
在异步编程中,当我们存在回调函数时,需要传递参数给回调函数,而当回调函数开始执行时原先传递而来的对象可能存在已经析构的风险。而如果我们传递的是个shared_ptr的指针,那么该对象在在传参的过程中引用计数加一,从而保证该对象在回调函数执行完成之前都不会被析构。
而回调函数的在类成员方法中调用的时候,就可以使用 shared_from_this 方法生成一个安全的shared_ptr对象,从而保证了当前对象在回调函数结束之前任然存在引用计数,使得对象不被析构。