惰性Log

技術系の話題を中心に書いています

std::shared_ptrでthisポインタを渡す

はじめに

以下のコードはデストラクタが二回呼ばれて二重解放してしまう。

#include <memory>
#include <iostream>

class Test
{
public:
  std::shared_ptr<Test> GetPtr(){return std::shared_ptr<Test>(this);}
};

int main()
{
 auto ptr = std::make_shared<Test>();
 auto ptr2 = ptr->GetPtr();
 std::cout << "shared counter:"<< ptr.use_count() << std::endl;
 return 0;
}

出力

実行環境:gcc 7.4.0

shared counter:1
double free or corruption (out)
Aborted (core dumped)

原因と対策

thisポインタをそのまま突っ込んでしまうと、参照カウンタが繰り上がらずにデストラクタが二回呼ばれてしまう。詳しい実装は各自で。

ではどうすれば良いかというと、そのためのクラスがstd::shared_ptrとともにC++11で追加された。それがstd::enable_shared_from_thisクラス。
これをpublic継承し、thisの代わりにshared_from_this()というメンバ関数を使うことで安全に操作することが可能となる。

以下がstd::enable_shared_from_thisを継承して書き直したもの。

#include <memory>
#include <iostream>

class Test : public std::enable_shared_from_this<Test>
{
public:
  std::shared_ptr<Test> GetPtr(){return shared_from_this();}
};

int main()
{
 auto ptr = std::make_shared<Test>();
 auto ptr2 = ptr->GetPtr();
 std::cout << "shared counter:"<< ptr.use_count() << std::endl;
 return 0;
}

出力

shared counter:2

これでちゃんと参照カウンタが動いてくれているようだ。相違点は、std::enable_shared_from_thisをpublic継承したところと、GetPtr()でshared_from_this()を返している点である。

注意点

std::enable_shared_from_thisクラスは自身もstd::shared_ptrで管理されているオブジェクトでないと機能しない。
もしそうでないオブジェクトで使用された場合、C++17以降では例外bad_weak_ptrが投げられる。

参考

cpprefjp.github.io

cpprefjp.github.io