searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

enable_shared_from_this简介与分析

2024-09-04 09:42:25
2
0

阅读/编写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. 使用总结

  1. public继承enable_shared_from_this模板类
  2. enable_shared_from_this类的头文件是memory
  3. 使用shared_from_this()在类里获取当前对象的智能指针
  4. 将类的构造函数置为private,并使用工厂函数create()来创建对象,create()函数为static,且返回值类型为shared_ptr<>

4. 参考文献

  1. Effective Modern C++
0条评论
作者已关闭评论
1****m
3文章数
0粉丝数
1****m
3 文章 | 0 粉丝
1****m
3文章数
0粉丝数
1****m
3 文章 | 0 粉丝
原创

enable_shared_from_this简介与分析

2024-09-04 09:42:25
2
0

阅读/编写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. 使用总结

  1. public继承enable_shared_from_this模板类
  2. enable_shared_from_this类的头文件是memory
  3. 使用shared_from_this()在类里获取当前对象的智能指针
  4. 将类的构造函数置为private,并使用工厂函数create()来创建对象,create()函数为static,且返回值类型为shared_ptr<>

4. 参考文献

  1. Effective Modern C++
文章来自个人专栏
Keep Moving
3 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0