std::enable_shared_from_this 以及使用注意

keywords

  1. std::enable_shared_from_this
  2. 线程安全
  3. std::weak_ptr

0 引言

C++11的一些高级性能,方便了我们的编程,但是面对这些高级特性的同时,也给我们埋了很多坑。比如,看下面这段代码:

#include 
#include 
#include 

class A {
 public:
  A(int val):val_(val){}
  std::shared_ptr GetPtr(){
    return std::shared_ptr(this);
  }

 private:
  int val_;
};

int main(){
  auto a = std::make_shared(1);
  auto b = a->GetPtr();

  return 0;
}

不知道你是否看出这段代码的问题? 我大概解释下:使用std::shared_ptr的时候,有一个注意事项一定一定要避免不同的ptr指向同一个对象
这个原则,大家想想也知道,好比,一台车卖给了2个人,那这个车到底归谁呢? 另外,比喻恰当点:当两个人都想卖这台车额时候,第一个人如果卖了,第二个人拿啥卖呢?

1 方案

有问题当然需要解决

1.1 enable_shared_from_this

但是我们很难避免需要把this指针当做std::shared_ptr参数传递的场景,怎么办呢?
C++当然针对这个问题,是有方案的,那就是使用 std::enable_shared_from_this,上面代码修改如下:

#include 
#include 
#include 

class A: public  std::enable_shared_from_this{
    public:
    A(int val):val_(val){}
    std::shared_ptr GetPtr(){
      return shared_from_this();
    }

    private:
    int val_;
};

int main(){
  auto a = std::make_shared(1);
  auto b = a->GetPtr();

  return 0;
}

到这里,关于将this指针当做参数传递的事情是解决了,但是接下来,继续看。

1.2 enable_shared_from_this 使用之坑

1.2.1 与构造函数之坑

禁止在构造函数中,使用。这个其实也好理解:对象都没构造完,哪里来的对象让你shared呢

1.2.2 与析构函数之坑

注意,在进入到析构函数之前,shared_ptr的reference count已经是0了,此时不能在访问之前shared_from_this()了

1.2.3 异步访问之坑

我们可能会有需求,将拿到的shared_ptr对象交给异步的线程来使用,此时,比如,看看下面代码:

#include 
#include 
#include 
#include 
#include 

struct A : public std::enable_shared_from_this {
  A() {
    inited_.store(true);
  }
  bool isInit() {
    return inited_.load();
  }
  void debug() {
    std::cout << "debug" << std::endl;
  }
  void init() {
    std::shared_ptr ptr = shared_from_this();
    thread_ = std::thread([ptr](){
        usleep(200*10000);
        ptr->debug();
    });
  }

  ~A() {
    std::cout << "pre ~A" << std::endl;
    usleep(1000 * 1000);
    inited_.store(false);
    std::cout << "wait ~A" << std::endl;
    if (thread_.joinable()) {
      thread_.join();
    }
    std::cout << "post ~A" << std::endl;
  }
 private:
  std::atomic inited_;
  std::thread thread_;
};

int main() {
  {
    auto a = std::make_shared();
    a->init();
    usleep(100 * 1000);
    a.reset();
  }
  std::cout << "will exit" << std::endl;
  sleep(5);
  std::cout << "exit" << std::endl;
  return 0;
}

发现没,程序执行异常crash了。还记得吗,对于继承了enable_shared_from_this的对象,不能在析构函数中使用对象,上面情况是不是命中了这个禁忌?

针对这个问题,weak_ptr能提供一个很好的解决方案,关于weak_ptr后面会专门介绍,使用weak_ptr方案的代码如下

#include 
#include 
#include 
#include 
#include 

struct A : public std::enable_shared_from_this {
  A() {
    inited_.store(true);
  }
  bool isInit() {
    return inited_.load();
  }
  void debug() {
    std::cout << "debug" << std::endl;
  }
  void init() {
    std::weak_ptr weak_ptr = shared_from_this();
    thread_ = std::thread([weak_ptr]() {
      usleep(1000 * 10000);
      auto this_ = weak_ptr.lock();
      if (this_) {
        this_->debug();
      } else {
        std::cout << "null of this_" << std::endl;
      }
    });
  }

  ~A() {
    std::cout << "pre ~A" << std::endl;
    usleep(1000 * 1000);
    inited_.store(false);
    std::cout << "wait ~A" << std::endl;
    if (thread_.joinable()) {
      thread_.join();
    }
    std::cout << "post ~A" << std::endl;
  }
 private:
  std::atomic inited_;
  std::thread thread_;
};

int main() {
  {
    auto a = std::make_shared();
    a->init();
    usleep(100 * 1000);
    a.reset();
  }
  std::cout << "will exit" << std::endl;
  sleep(5);
  std::cout << "exit" << std::endl;
  return 0;
}

关于weak_ptr 以及其方法 lock() ,可以先参考 std::weak_ptr


支持原创,转载请附上原文链接


你可能感兴趣的:(std::enable_shared_from_this 以及使用注意)