使用するスマートポインターを決定することは、所有権の問題です。リソース管理に関しては、オブジェクトBの存続期間を制御している場合、オブジェクトA はオブジェクトBを所有します。たとえば、メンバー変数の存続期間はオブジェクトの存続期間に関連付けられているため、メンバー変数はそれぞれのオブジェクトによって所有されます。オブジェクトの所有方法に基づいてスマートポインタを選択します。
ソフトウェアシステムの所有権は、ソフトウェアの外部で考えるのとは異なり、所有権とは別のものであることに注意してください。たとえば、人が自分の家を「所有」しているPerson
場合でも、オブジェクトがオブジェクトの寿命を制御できるとは限りませんHouse
。これらの現実世界の概念をソフトウェアの概念と融合させることは、自分を穴にプログラムする確実な方法です。
オブジェクトの唯一の所有権がある場合は、を使用しますstd::unique_ptr<T>
。
オブジェクトの所有権を共有している場合...-所有権に
循環がない場合は、を使用しますstd::shared_ptr<T>
。
-サイクルがある場合は、「方向」を定義し、std::shared_ptr<T>
一方の方向と他方の方向で使用しstd::weak_ptr<T>
ます。
オブジェクトがあなたを所有しているが、所有者がいない可能性がある場合は、通常のポインターT*
(親ポインターなど)を使用します。
オブジェクトがあなたを所有している(または存在が保証されている)場合は、referenceを使用しますT&
。
警告:スマートポインターのコストに注意してください。メモリまたはパフォーマンスが制限された環境では、メモリを管理するためのより手動のスキームで通常のポインタを使用することが有益な場合があります。
コスト:
- カスタムの削除機能がある場合(割り当てプールを使用する場合など)は、ポインターごとにオーバーヘッドが発生しますが、手動で削除することで簡単に回避できます。
std::shared_ptr
コピー時に参照カウントが増加し、破棄時に減少してから、保持されたオブジェクトを削除する0カウントチェックのオーバーヘッドがあります。実装によっては、これによりコードが肥大化し、パフォーマンスの問題が発生する可能性があります。
- コンパイル時間。すべてのテンプレートと同様に、スマートポインターはコンパイル時間に悪影響を及ぼします。
例:
struct BinaryTree
{
Tree* m_parent;
std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};
バイナリツリーはその親を所有していませんが、ツリーの存在はその親(またはnullptr
ルート)の存在を意味するため、通常のポインターを使用します。(値のセマンティクスを持つ)は、バイナリツリーは、その子の唯一の所有権を持っているので、それらがありますstd::unique_ptr
。
struct ListNode
{
std::shared_ptr<ListNode> m_next;
std::weak_ptr<ListNode> m_prev;
};
ここでは、リストノードが次のリストと前のリストを所有しているため、方向を定義し、shared_ptr
次へweak_ptr
と前への方向を使用してサイクルを壊します。