vectorとunique_ptr その2
前回はコピーすべきところでムーブすべきではないことがわかった。
コードはshared_ptrを用いることで、すべてがうまくいくようになる。
class C { public: shared_ptr<int> d; C() : d(new int(0)) {} C( const int n ) : d(new int(n)) {} // コピーコンストラクタ、オペレータ=は自動生成でok ~C() {} }; bool operator<(const C& lhs, const C& rhs ) { return lhs.d.get() < rhs.d.get(); } void test_code() { vector<C> v; C a(3), b(2), c(1); v.push_back(a); v.push_back(b); v.push_back(c); sort( v.begin(), v.end() ); // OK }
ただし、shared_ptrは生のポインターやunique_ptrを用いる場合と比べてリソースやスピードなどのコストがかかる。
ポインターをムーブさせることがだめなのであるから、コピーはコピーということに立ち返れば、unique_ptrを使うこともできる。
class C { public: unique_ptr<int> d; C() : d(new int(0)) {} C( const int n ) : d(new int(n)) {} C( const C& lhs ) : d(new int(*lhs.d.get())) {}// コピーコンストラクタ const C& operator=( const C& lhs ) // 代入 { *d.get() = *lhs.d.get(); return *this; } ~C() {} }; bool operator<(const C& lhs, const C& rhs ) { return *lhs.d.get() < *rhs.d.get(); } void test_code() { vector<C> v; C a(3), b(2), c(1); v.push_back(a); v.push_back(b); v.push_back(c); sort( v.begin(), v.end() ); // OK }
上記コードでは、コピーでポインターをムーブさせるのではなく、ポインターの先をコピーさせるように変更している。
どちらがよいのだろうか?
shared_ptr版は、ポインターの先を共有するので、ポインターのコピーとしてはサイズが大きくなりかつ低速となる。しかしオブジェクトのコピーを行わない。
unique_ptr版は、ポインターの先を共有しない。このためポインターのコピーとは別にオブジェクトの生成コストがかかる。通常オブジェクトの生成はポインターのコピーよりずっと遅い。
ポインターを使いたくなるシーンを考えると、class Cの実装はshared_ptr版の方がメモリーサイズも速度も速くなる可能性が高いと思われる。また、書くべきコードも少なく見通しもよいだろう。
unique_ptr版の方がよい場合は、vectorにすべて異なるオブジェクトを格納し、かつ静的なコンテナ(変更しない読み出し専用)として用いる場合だけではないだろうか。