std::enable_shared_from_this 以及使用注意

keywords

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

0 引言

C++11的一些高級(jí)性能,方便了我們的編程,但是面對(duì)這些高級(jí)特性的同時(shí),也給我們埋了很多坑。比如,看下面這段代碼:

#include <iostream>
#include <memory>
#include <unistd.h>

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

 private:
  int val_;
};

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

  return 0;
}

不知道你是否看出這段代碼的問(wèn)題? 我大概解釋下:使用std::shared_ptr的時(shí)候,有一個(gè)注意事項(xiàng)一定一定要避免不同的ptr指向同一個(gè)對(duì)象
這個(gè)原則,大家想想也知道,好比,一臺(tái)車賣給了2個(gè)人,那這個(gè)車到底歸誰(shuí)呢? 另外,比喻恰當(dāng)點(diǎn):當(dāng)兩個(gè)人都想賣這臺(tái)車額時(shí)候,第一個(gè)人如果賣了,第二個(gè)人拿啥賣呢?

1 方案

有問(wèn)題當(dāng)然需要解決

1.1 enable_shared_from_this

但是我們很難避免需要把this指針當(dāng)做std::shared_ptr參數(shù)傳遞的場(chǎng)景,怎么辦呢?
C++當(dāng)然針對(duì)這個(gè)問(wèn)題,是有方案的,那就是使用 std::enable_shared_from_this,上面代碼修改如下:

#include <iostream>
#include <memory>
#include <unistd.h>

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

    private:
    int val_;
};

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

  return 0;
}

到這里,關(guān)于將this指針當(dāng)做參數(shù)傳遞的事情是解決了,但是接下來(lái),繼續(xù)看。

1.2 enable_shared_from_this 使用之坑

1.2.1 與構(gòu)造函數(shù)之坑

禁止在構(gòu)造函數(shù)中,使用。這個(gè)其實(shí)也好理解:對(duì)象都沒(méi)構(gòu)造完,哪里來(lái)的對(duì)象讓你shared呢

1.2.2 與析構(gòu)函數(shù)之坑

注意,在進(jìn)入到析構(gòu)函數(shù)之前,shared_ptr的reference count已經(jīng)是0了,此時(shí)不能在訪問(wèn)之前shared_from_this()了

1.2.3 異步訪問(wèn)之坑

我們可能會(huì)有需求,將拿到的shared_ptr對(duì)象交給異步的線程來(lái)使用,此時(shí),比如,看看下面代碼:

#include <iostream>
#include <memory>
#include <unistd.h>
#include <thread>
#include <atomic>

struct A : public std::enable_shared_from_this<A> {
  A() {
    inited_.store(true);
  }
  bool isInit() {
    return inited_.load();
  }
  void debug() {
    std::cout << "debug" << std::endl;
  }
  void init() {
    std::shared_ptr<A> 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<bool> inited_;
  std::thread thread_;
};

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

發(fā)現(xiàn)沒(méi),程序執(zhí)行異常crash了。還記得嗎,對(duì)于繼承了enable_shared_from_this的對(duì)象,不能在析構(gòu)函數(shù)中使用對(duì)象,上面情況是不是命中了這個(gè)禁忌?

針對(duì)這個(gè)問(wèn)題,weak_ptr能提供一個(gè)很好的解決方案,關(guān)于weak_ptr后面會(huì)專門(mén)介紹,使用weak_ptr方案的代碼如下

#include <iostream>
#include <memory>
#include <unistd.h>
#include <thread>
#include <atomic>

struct A : public std::enable_shared_from_this<A> {
  A() {
    inited_.store(true);
  }
  bool isInit() {
    return inited_.load();
  }
  void debug() {
    std::cout << "debug" << std::endl;
  }
  void init() {
    std::weak_ptr<A> 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<bool> inited_;
  std::thread thread_;
};

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

關(guān)于weak_ptr 以及其方法 lock() ,可以先參考 std::weak_ptr


支持原創(chuàng),轉(zhuǎn)載請(qǐng)附上原文鏈接


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容