阅读/编写C++代码时,我们可能会碰到enable_shared_from_this这个类,通常与之一起出现的,是shared_from_this()函数。那么这两个东西到底是什么?又有什么作用呢?让我们在本文里一探究竟吧。
1. 概念介绍
从enable_shared_from_this的名字上看,enable表示开启某个功能,shared指shared_ptr,this代表this指针。所以从名字中可以推测这个类的作用是开启一个功能,这个功能将this转换为shared_ptr指针。但是this指针明明可以直接转为对应class的shared_ptr,为何要大费周折地弄这样一个类呢?
2. 使用背景描述
在智能指针大行其道的今天,使用智能指针自动管理对象的生命周期是再自然不过的了。使用智能指针,后文特指std::shared_ptr,需要注意避免一个大坑,就是使用同一个裸指针初始化两个不同的std::shared_ptr。这样做带来的后果就是,std::shared_ptr指向的对象,会被析构两次,进而引发未定义行为。
auto pw = new Widget;
std::shared_ptr<Widget> spw1(pw);
std::shared_ptr<Widget> spw2(pw);
一个由此而来,且不明显的一个坑是这样的。有一个class为Widget,它的定义如下:
class Widget{
public:
void process();
...
private:
vector<shared_ptr<Widget>> vec_;
}
process内部使用到指向自身的裸指针this,并且我们希望使用针对this的shared_ptr,代码如下
void Widget::process(){
... // 做一些处理
vec_.emplace_back(this); // 这里会隐式转换this指针(Widget*类型)为shared_ptr<Widget>类型
}
如果此时外部已经有个一个指向该Widget对象的shared_ptr,那么就会出现两个shared_ptr同时指向一个裸指针的情况,并且在Widget对象析构时出现未定义问题。代码如下
...
{
auto pw = make_shared<Widget>();
pw->process();
}
// 当程序运行到这里的时候,会出现对pw指向的Widget对象的两次析构。
现在问题已经描述完毕,解决方案也很明确,就是使用enable_shared_from_this这个类,并使用shared_from_this()函数在上文的process()函数里获取指向Widget自身的智能指针,而不是this这个裸指针了。enable_shared_from_this的使用方法如下
// step 1. Widget类public继承enable_shared_from_this
class Widget : public enable_shared_from_this<Widget>
{
public:
void process();
...
private:
vector<shared_ptr<Widget>> vec_;
}
process函数改动如下
// step 2. 使用shared_from_this()函数
void Widget::process(){
... // 做一些处理
vec_.emplace_back(shared_from_this());
}
目前为止,Widget类析构时就不会再出现两次析构的问题了。但是新的问题出现了,调用shared_from_this()时,必须确保Widget对象已经存在一个shared_ptr指向它了,否则shared_from_this()会抛出异常。为了解决这个问题,一个常用的方法是,使用static create()函数来创建Widget对象,且返回值是shared_ptr(如果返回unique_ptr,必须将其upgrade(使用lock()成员函数)到shared_ptr),如此一来,就能保证Widget对象创建后,就立马有一个shared_ptr指向它。
class Widget : public enable_shared_from_this<Widget>
{
public:
static shared_ptr<Widget> create(); // 通过static工厂函数返回shared_ptr<Widget>类型的值,
// 确保Widget创建后立马拥有一个shared_ptr指向这个Widget
void process();
...
private:
Widget() = default; // 把构造函数移动到private
vector<shared_ptr<Widget>> vec_;
}
使用方式如下
...
{
auto pw = Widget::create(); // 使用create函数来创建指向Widget对象的shared_ptr
pw->process();
}
... // 程序运行一切正常
3. 使用总结
- public继承enable_shared_from_this模板类
- enable_shared_from_this类的头文件是memory
- 使用shared_from_this()在类里获取当前对象的智能指针
- 将类的构造函数置为private,并使用工厂函数create()来创建对象,create()函数为static,且返回值类型为shared_ptr<>
4. 参考文献
- Effective Modern C++