私は大きなC++コード(〜2300ファイル、〜600K行、主に古いC/C++ 98スタイルのコード)をリファクタリングしている最中で、C++スマートポインターを使用して補強できるメモリリークが確実にあります。スマートポインターへの移行に向けた段階的な経路はありますか、それとも「オールオアナッシング」の提案ですか?
たとえば、すべての「ファクトリクラス」は_std::unique_ptr
_を返す必要がありますが、これにより、(適切に)すべての呼び出し元が結果を_std::unique_ptr
_として保存するよう強制されます。しかし、ローカルコードは、それを処理するために生のptr(ローカルの弱いptrとして扱われる)を取得するだけです。 (複数の)データ構造内にポインタを格納する場合、_std::shared_ptr
_を使用する必要がある同様のパスをたどることもできるようです-たとえば、_std::shared_ptr
_(および_std::weak_ptr
_を使用して後方参照)。ローカルウィークポインターには生のポインターを使用します。
徐々に移行することはできますが、後で境界をクリーンアップするのは難しい場合があります。
古いコードを書き直す前に、C++コアガイドラインを参照してください。非常に賢明なガイダンスが数多く提供されているためです。例えば:
T*
_または_T&
_引数を使用多くの場合、参照を使用する関数の引数を除いて、生のポインタを使用することは、特に借用/一時的な所有権を示すために依然として適切です。
したがって、一度に1つの関数とファイルを移行し、内部とインターフェースを最新化することをお勧めします。スマートポインターとの間で変換を行うときに、呼び出しサイトをこれらの関数に修正します。たとえば、関数T* create()
をunique_ptr<T> create()
に更新すると、呼び出しサイトはcreate()
からcreate().release()
に更新されます。その部分も再近代化します。
残念ながら、_shared_ptr
_が削除の原因となるため、共有所有権ではこれはあまりうまく機能しませんが、理想的には、このような複雑な所有権が発生することはほとんどありません。すべての共有者を一度に更新する必要があります。疑わしい場合は、これらの複雑な部分を後で使用するために残し、最初に、スマートポインターが大きなリスクなしに貴重な明快さをもたらす機能を攻撃します。
もちろん、一般的なリファクタリングのベストプラクティスが適用されます。例:
T* create()
をunique_ptr<T> create()
に直接変更する代わりに:unique_ptr<T> create2(); T* create() { return create2().release(); }
のような互換性レイヤーを作成することから始めます。create2()
に徐々に移動します。create2
_→create
の名前を一括変更します。この取り組みにおける大きな「リスク」は、メモリの安全性の問題に遭遇することです。解放後使用。その場合は、すぐに修正するのが最も簡単です。これは、インクリメンタルリファクタリングアプローチと連携します。これにより、すべての2kファイルの処理が完了する前に、メンテナンスリリースを作成できるようになるからです。
リファクタリングの範囲が広い場合、スマートポインタに完全に移動するのは賢明ではないかもしれません。これは、実質的な価値をもたらす場合にのみ行ってください。スマートポインタの価値は、正しいコードを簡単に記述できることです。したがって、将来変更される可能性のあるコンポーネントに集中して取り組みます。対照的に、10年間ほとんど変更されていない場合は、コードを変更しないでください(現時点では)。