スポンサーリンク
デリータの指定なしで作った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の生成権限(業務上)がある場合はどうしようもない」という事を意味する。だから被害者を減らすために、スマートポインタの扱いには注意を払うべきである。
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(); }
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; }