スポンサーリンク

細工なしのstd::shared_ptrからポインタの管理を剥奪することは絶対にできないので注意する事

デリータの指定なしで作ったshared_ptrからポインタを奪い取ることはどうあがいてもできそうにない。unique_ptrをshared_ptrへ変換することも、uniue_ptrから生ポインタを解放することもできるが、shared_ptrはshared_ptrから変更できない。

#include <memory>

void pattern1() {

  std::unique_ptr<int> unique_p(new int);

  // OK 管理を shared_ptrへ変更
  std::shared_ptr<int> shared_p = std::move(unique_p);

}
           
void pattern2() {

  std::unique_ptr<int> unique_p(new int);

  // OK 管理を自ら行う
  int* p = unique_p.release();

}
      
void pattern3() {

  std::shared_ptr<int> shared_p(new int);

  // NG releaseは存在しない
  int* p = shared_p.release();

  // NG unique_ptr への変換はできない
  std::unique_ptr<int> unique_p(std::move(shared_p));

}

これはつまり、「自分以外にshared_ptrの生成権限(業務上)がある場合はどうしようもない」という事を意味する。だから被害者を減らすために、スマートポインタの扱いには注意を払うべきである。

例1 Factory関数からはunique_ptrを返す

unique_ptrであれば、少なくともshared_ptrに意図的に変えるまではどうとでもできる。他者が使うかもしれない自コードがshared_ptrを生成して返すような真似は、未来に誰かを不幸にする可能性があるので避けるようにする。

#include <iostream>

#include <memory>

// unique_ptrを返すFactory。後でいろいろと融通が利く
std::unique_ptr<int> Factory_unique() {

  return std::make_unique<int>();
}
       
void job_happy() {

  auto uptr = std::move(Factory_unique());

  *uptr = 5;
  printf("%d\n", *uptr);

  // OK int* ptr = uptr.release();
  // OK std::shared_ptr<int> sptr = std::move(uptr);

}
      
// shared_ptrを返すFactory。融通が利かない
std::shared_ptr<int> Factory_shared() {

  return std::make_shared<int>();
}
      
void job_unhappy() {

  auto sptr = Factory_shared();

  *sptr = 7;
  printf("%d\n", *sptr);

  //NG int* ptr = sptr.release();
  //NG std::unique_ptr<int> sptr = std::move(sptr);

}
      
int main()
{

  job_happy();

  job_unhappy();

}

例2 デリータにポインタを解放できるようなオブジェクトを指定

get_deleterを使えばデリータを取り出すことができる。メモリを解放するかしないかを選択できるクラスのインスタンスをデリータにしておけば、その指示を出すことができるようになる。

#include <iostream>

#include <memory>

// オブジェクトを削除しない場合を指定できるデリータ
struct ReleasableDeleter {

  // 削除しない場合はtrue
  bool _release;

  ReleasableDeleter() :_release(false) {}

  template <class T>
  void operator()(T* p)
  {
    if (_release == false) {
      delete p;
    }
  }
};
      
// テスト用のデータ型
class Object {
public:
  const char* _name;
  Object(const char* name) :_name(name){}
  ~Object() { 
    std::cout << "delete " << _name << std::endl;
  
  }
};
      
int main()
{
  Object* valid;
  {
    std::shared_ptr<Object> p1(new Object("obj1"), ReleasableDeleter());
    std::shared_ptr<Object> p2(new Object("obj2"), ReleasableDeleter());

    // 削除しない設定
    ReleasableDeleter* deleter = std::get_deleter<ReleasableDeleter>(p1);
    deleter->_release = true;

    valid = p1.get();
  }

  std::cout << "まだ生きている: " << valid->_name << std::endl;

  delete valid;

  //std::cout << "解放済み: " << valid->_name << std::endl;

}
delete obj2
まだ生きている: obj1
delete obj1

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: