SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

「never say new, never say delete」
~C++11のunique_ptrのおはなし

unique_ptr ひとめぐり

配列への対応とmake_unique

 auto_ptrにはできなかったことをもう一つ。

 auto_ptrでは配列を確保することができません...いや、確保された配列を正しく解放できません。auto_ptrのデストラクタは捕まえているポインタに対しdelete p;するんですけど、配列に対してはdelete[] p;ですからね。

 unique_ptrならテンプレート引数に(foo[]のように)配列を与えておけば、デストラクト時にちゃんとdelete[]してくれます。

list05
int main() {
  const int N = 3;
  unique_ptr<foo[]> p(new foo[N]);
  for ( int i = 0; i < N; ++i ) { cout << p[i].symbol() << ' '; }
  cout << endl;
}

/* 実行結果
foo(0) [BORN]
foo(0) [BORN]
foo(0) [BORN]
foo(0) foo(0) foo(0)
foo(0) [DEAD]
foo(0) [DEAD]
foo(0) [DEAD]
*/

 さらに、Visual C++ 2013にはC++14でサポート予定のmake_uniqueが一足お先に追加されています。これ使えばdeleteだけでなくnewともオサラバできます。

list06
int main() {
  unique_ptr<foo> p;
  p = make_unique<foo>();           // new foo()
  p = make_unique<foo>("adam");     // new foo("adam")
  p = make_unique<foo>("eve", 19);  // new foo("eve",19)
  const int N = 3;
  auto q = make_unique<foo[]>(N);   // new foo[N]
  for ( int i = 0; i < N; ++i ) { cout << q[i].symbol() << ' '; }
  cout << endl;
}

/* 実行結果
foo(0) [BORN]
adam(0) [BORN]
foo(0) [DEAD]
eve(19) [BORN]
adam(0) [DEAD]
foo(0) [BORN]
foo(0) [BORN]
foo(0) [BORN]
foo(0) foo(0) foo(0)
foo(0) [DEAD]
foo(0) [DEAD]
foo(0) [DEAD]
eve(19) [DEAD]
*/

カスタム・デリータ

 unique_ptrの定義はこんなカンジになっています:

namespace std {
  template<typename T, typename D =default_delete<T>>
  class unique_ptr { ... };
}

 テンプレート引数は実は2つあって、2つめのDがポインタの解放を行うデリータとして機能します。デフォルトで用いられるstd::default_deleteは:

namespace std {
  template<typename T>
  struct default_delete {
    void operator()(T* p) const { delete p; }
  };
}

のような定義です。unique_ptrはポインタを手放す際にデリータのoperator()を呼ぶことで後始末を行っています。

 なので、このデリータ(後始末オブジェクト)を差し替えれば、delete以外の後始末をやらせることができます。

 delete p;のかわりにfclose(p);してくれるデリータを用意すれば、使い終わったら勝手にクローズされるFILE*が実現できますね、クラス・テンプレートの特殊化を使ってやってみましょう。

list07
// specialization
template<> struct std::default_delete<FILE> {
  void operator()(FILE* p) const {
    fclose(p);
    cout << "the FILE is closed." << endl;
  }
};

int main() {
  auto p = make_unique<foo>("adam",20);
  auto file = unique_ptr<FILE>(fopen("foo.txt","w"));
  fprintf(file.get(), "I am %s\n", p->symbol().c_str());
}
/* 実行結果
adam(20) [BORN]
the FILE is closed.
adam(20) [DEAD]
*/

 あるいは例えばdelete p;のかわりにCloseHandle(p);すれば、使い終わったら勝手にクローズしてくれるWindows HANDLEが実現できます。この場合、HANDLEの正体はvoid*なのでクラス・テンプレートの特殊化だとあらゆるvoid*に対してCloseHandleしてしまって迷惑千万。HANDLE専用のカスタム・デリータを作り、unique_ptrのコンストラクタに与えることにします。

list08
#include <Windows.h>
#include <iostream>
#include <memory>
#include <functional>

using namespace std;

// custom deleter
typedef std::unique_ptr<void,std::function<void(HANDLE)>> unique_win_handle;

unique_win_handle make_win_thread(
  LPTHREAD_START_ROUTINE routine,
  LPVOID                 arg,
  LPDWORD                pID = nullptr) {
  // ラムダ式で定義したカスタム・デリータを与える
  return unique_win_handle(
           CreateThread(nullptr, 0, routine, arg, 0, pID),
           [](HANDLE h) { CloseHandle(h); cout << "the HANDLE closed\n"; });
}

DWORD __stdcall thr_fun(LPVOID arg) {
  int n = *static_cast<int*>(arg);
  for ( int i = 0; i < n; ++i ) {
    cout << "something wonderful!!\n";
  }
  return 0;
}

int main() {
  int n = 3;
  auto thr = make_win_thread(&thr_fun, &n);
  WaitForSingleObject(thr.get(), INFINITE);
}
/* 実行結果
something wonderful!!
something wonderful!!
something wonderful!!
the HANDLE closed
*/

 ...いかがなもんでしょ、これだけ手間要らずになったC++、毛嫌いされる理由がまた1つ減ってくれたかしら。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/7479 2013/11/14 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー