Boost.WeakPtr を通常のポインタの代替として使う
例えば数百万行ぐらいの大きいソースがあって、そこでは生のポインタがバリバリ使われていたとする。
で、あるマネージャクラスが生成したオブジェクトのポインタをいろんなところにコピーしているので、終了時にマネージャクラスがそのポインタを先に削除するので、終了時に不正アクセスになってしまうのがよく問題になっていたとする。
しかも、マネージャクラスがオブジェクトを生成してマネージャクラスがそのオブジェクトを削除するという仕組みは決して変えられないとする。
class Manager { private: // Hoge と Fuga の生存期間は Manager と同じでなければならない Hoge m_hoge; Fuga m_fuga; public: Hoge* GetHoge() { return &m_hoge; } Fuga* GetFuga() { return &m_fuga; } };
この場合は生存期間が明確なので、Hoge, Fuga を boost::shared_ptr にするわけにはいかない。
現在は Manager クラスが既に死んでいるかどうかを見るために別のクラスからそれを問い合わせる手段を用意しているのだけれども、どうも一般的な解決方法ではないし、不正な領域を指しているポインタを保持しておくのもあまり良くない気がする。
また、Manager クラスが死んでいるかどうかをチェックしていない場合もあったりするのだけれども、プロジェクトが大きすぎるためそれを把握するも難しい。
そこで、そのオブジェクトの生成は boost::shared_ptr を使うようにして、それ以降の受け渡しは全て boost::weak_ptr で渡すようにすれば、boost::weak_ptr はポインタが生きているかどうかチェックできるので、どこにコピーしても大丈夫になる。
class Manager { private: // Hoge と Fuga の生存期間は Manager と同じでなければならない boost::shared_ptr<Hoge> m_hoge; boost::shared_ptr<Fuga> m_fuga; public: Manager() : m_hoge(new Hoge()), m_fuga(new Fuga()) { } boost::weak_ptr<Hoge> GetHoge() { return m_hoge; } boost::weak_ptr<Fuga> GetFuga() { return m_fuga; } };
class Moke { private: boost::weak_ptr<Hoge> m_hoge; public: Moke() : m_hoge(::GetManager()->GetHoge()) { } void Foo() { boost::shared_ptr<Hoge> hoge = m_hoge->lock(); // 既に Hoge は存在していない if (!hoge) return; hoge->Bar(); } };
つまり、あるポインタの指すオブジェクトが有効かどうか分からない可能性のあるポインタは boost::weak_ptr に変更してみるべしと。
この方針でちょっとリファクタリングを考えよう。