boost::scoped_ptr は std::auto_ptr のより安全な代替として便利に使えるクラスですが、
- release 出来ない
- 削除関数を指定できない
- 関数の戻り値として使えない
- 配列の時は scoped_array を使わなければならない
等、いくつか不満点があります。
幸いなことに、 C++0x には これらの不満点が全て解決された std::unique_ptr がある訳ですが、
C++0x なんか待ってられない、便利なスマートポインタは今すぐ使いたいんだ、って人も多いはず。
実際、趣味で C++ をやってる僕のような人とは違い、仕事で C++ を使っている場合、
C++0x の規格が固まって、コンパイラも対応を進めて、 C++0x を使ったコーディングがメジャーになる、
それまでには、あと何年もかかるのは必至です。
というわけで、上記の不満点を解決した scoped_ptr を、ちょいちょいと作ってみました。
http://gist.github.com/713685
使い方は、こんな感じです:
#include "scoped_ptr.hpp" #include <iostream> #include <algorithm> #include <cstdio> #include <boost/noncopyable.hpp> struct hoge : private boost::noncopyable { hoge( int i_ ) : i(i_) { std::cout << "hoge (" << i << "): Hello!\n"; } ~hoge() { std::cout << "hoge (" << i << "): Bye.\n"; } void foo() { std::cout << "hoge (" << i << "): Foo!\n"; } int i; }; void basic_usage() { { // 格納されたオブジェクトは、自動的に削除される etude::scoped_ptr<hoge> p( new hoge(1) ); p->foo(); } { etude::scoped_ptr<hoge> p1( new hoge(2) ); // move() を使うことで所有権を移動できる( move すると空になる) etude::scoped_ptr<hoge> p2( p1.move() ); BOOST_ASSERT( !p1 && p2 ); // etude::scoped_ptr<hoge> p2 = p1.move(); とは残念ながら書けない // でも代入演算は素直に書ける(けど const に出来ないから一長一短) p1 = p2.move(); BOOST_ASSERT( p1 && !p2 ); // boost::scoped_ptr みたいに swap するとか。 swap( p1, p2 ); // p1.swap(p2); でもいい BOOST_ASSERT( !p1 && p2 ); // foo! p2->foo(); } { // 格納しているオブジェクトを解放することも出来る。 etude::scoped_ptr<hoge> p( new hoge(3) ); hoge* const p_ = p.release(); BOOST_ASSERT( !p ); // 忘れずに delete してあげないとあげないと delete p_; } } void advanced_usage(); // ファクトリ。戻り値は move_ptr<T> を使う etude::move_ptr<hoge> create_hoge( int i ) { return etude::scoped( new hoge(i) ); // move_ptr を作るヘルパ関数 } // deleter struct file_closer { void operator()( std::FILE* file ) const { std::fclose( file ); } }; void advanced_usage() { { // ファクトリを使ってオブジェクトをつくると、安全に取り扱える etude::scoped_ptr<hoge> p( create_hoge(4) ); create_hoge(5); // ちゃんと解放される! // foo! p->foo(); } { // テンプレート引数に T[] を指定すれば、配列を格納することも出来る etude::scoped_ptr<int[]> a( new int[6] ); // operator[] と operator+ (こっちは微妙だが)が用意されている a[0] = 0; std::fill( a+1, a+6, 1 ); BOOST_ASSERT( a[1] == 1 ); // 削除は勿論 delete [] が呼ばれる } { // カスタムデリータを使った例。 etude::scoped_ptr<std::FILE, file_closer> const f_( std::tmpfile() ); std::FILE* const f = f_.get(); // 簡便のために // 適当に読み書き std::fprintf( f, "hogehoge" ); // and so on... // 自動的に fclose される } } int main() { basic_usage(); advanced_usage(); }
実装は結構トリッキーなので、コンパイラによっては動かないかもしれません。
特に move_ptr の値渡し周りは、これで正しいかどうかが不安だったり。
というわけで、動かないようなら std::auto_ptr の実装を参考に、自前でなんとかしてください><
// っていうか http://home.roadrunner.com/~hinnant/unique_ptr03.html を使えばいんじゃね的な