スマートポインターとは何ですか?いつ使用すべきですか?
スマートポインターとは何ですか?いつ使用すべきですか?
回答:
更新
この答えはかなり古く、そのため、当時「良かった」ものを説明しています。これは、Boostライブラリによって提供されたスマートポインターでした。C ++ 11ので、標準ライブラリには、十分なスマートポインタの種類を提供している、とあなたはの使用を支持すべきであるstd::unique_ptr
、std::shared_ptr
とstd::weak_ptr
。
もありましたstd::auto_ptr
。これはスコープ付きポインタに非常に似ていましたが、コピーされる「特別な」危険な機能も持っていました。
C ++ 11で非推奨になり、C ++ 17で削除されたため、使用しないでください。
std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership.
// p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.
古い答え
スマートポインターは、「生」(または「ベア」)C ++ポインターをラップするクラスであり、ポイントされているオブジェクトの有効期間を管理します。単一のスマートポインター型はありませんが、それらすべてが実用的な方法で生のポインターを抽象化しようとします。
生のポインターよりもスマートポインターを優先する必要があります。ポインターを使用する必要があると思われる場合(実際に使用するかどうかを最初に検討してください)、通常はスマートポインターを使用することをお勧めします。これにより、生のポインターに関する多くの問題が軽減されます。
生のポインタを使用すると、プログラマはオブジェクトが不要になったときに明示的に破棄する必要があります。
// Need to create the object to achieve some goal
MyObject* ptr = new MyObject();
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?
比較によるスマートポインターは、オブジェクトが破棄されるタイミングに関するポリシーを定義します。それでもオブジェクトを作成する必要がありますが、オブジェクトを破棄することを心配する必要はありません。
SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.
// Destruction of the object happens, depending
// on the policy the smart pointer class uses.
// Destruction would happen even if DoSomething()
// raises an exception
使用中の最も単純なポリシーには、boost::scoped_ptr
またはによって実装されるような、スマートポインターラッパーオブジェクトのスコープが含まれますstd::unique_ptr
。
void f()
{
{
std::unique_ptr<MyObject> ptr(new MyObject());
ptr->DoSomethingUseful();
} // ptr goes out of scope --
// the MyObject is automatically destroyed.
// ptr->Oops(); // Compile error: "ptr" not defined
// since it is no longer in scope.
}
std::unique_ptr
インスタンスはコピーできないことに注意してください。これにより、ポインターが複数回(誤って)削除されるのを防ぎます。ただし、それへの参照を、呼び出す他の関数に渡すことはできます。
std::unique_ptr
sは、オブジェクトの存続期間を特定のコードブロックに関連付けたい場合や、別のオブジェクト内のメンバーデータとして埋め込んだ場合は、その他のオブジェクトの存続期間に役立ちます。オブジェクトは、コードの包含ブロックが終了するまで、または包含オブジェクト自体が破棄されるまで存在します。
より複雑なスマートポインターポリシーには、ポインターの参照カウントが含まれます。これにより、ポインタをコピーできます。オブジェクトへの最後の「参照」が破棄されると、オブジェクトは削除されます。このポリシーはboost::shared_ptr
およびによって実装されていstd::shared_ptr
ます。
void f()
{
typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
MyObjectPtr p1; // Empty
{
MyObjectPtr p2(new MyObject());
// There is now one "reference" to the created object
p1 = p2; // Copy the pointer.
// There are now two references to the object.
} // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero.
// The object is deleted.
参照カウントポインターは、オブジェクトの存続期間がはるかに複雑で、コードの特定のセクションや別のオブジェクトに直接関連付けられていない場合に非常に役立ちます。
カウントされたポインタを参照することには1つの欠点があります—ぶら下がっている参照が作成される可能性があります。
// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!
別の可能性は循環参照を作成することです:
struct Owner {
std::shared_ptr<Owner> other;
};
std::shared_ptr<Owner> p1 (new Owner());
std::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1
// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!
この問題を回避するために、BoostとC ++ 11の両方でを定義して、weak_ptr
への弱い(カウントされていない)参照を定義していますshared_ptr
。
std::auto_ptr<MyObject> p1 (new MyObject());
の代わりにstd::auto_ptr<MyObject> p1 (new Owner());
?
const std::auto_ptr
C ++ 03に悩まされている場合は、A を使用しても安全です。C ++ 11にアクセスできるようになるまで、pimplパターンにかなり使用しました。
最近のC ++(C ++ 11以降)の最近の簡単な答えは次のとおりです。
std::unique_ptr
同じオブジェクトへの複数の参照を保持するつもりがない場合に使用します。たとえば、あるスコープに入るときに割り当てられ、スコープから出るときに割り当て解除されるメモリへのポインタにそれを使用します。std::shared_ptr
複数の場所からオブジェクトを参照したい場合、およびこれらの参照がすべてなくなるまでオブジェクトの割り当てを解除したくない場合に使用します。std::weak_ptr
複数の場所からオブジェクトを参照したい場合に使用します-無視して割り当てを解除してもよい参照の場合(つまり、逆参照しようとするとオブジェクトがなくなっていることに注意してください)。boost::
スマートポインターを使用したり、std::auto_ptr
必要に応じて読み上げたりできる特別な場合を除いて、使用しないでください。T*
にあるstd::unique_ptr<T>
何std::weak_ptr<T>
にあるstd::shared_ptr<T>
スマートポインターは、自動メモリ割り当て解除、参照カウントなどのいくつかの追加機能を備えたポインターのようなタイプです。
小さなイントロページで提供されていますスマートポインタ-何を、なぜ、どのましたか?。
単純なスマートポインター型の1つはstd::auto_ptr
(C ++標準のチャプター20.4.5)です。これにより、スコープ外のときにメモリを自動的に割り当て解除でき、例外がスローされたときの単純なポインターの使用よりも堅牢ですが、柔軟性は低くなります。
別の便利なタイプはboost::shared_ptr
、参照カウントを実装し、オブジェクトへの参照が残っていないときに自動的にメモリの割り当てを解除するタイプです。これは、メモリリークの回避に役立ち、RAIIの実装に簡単に使用できます。
サブジェクトは、David Vandevoorde、Nicolai M. Josuttis著の「C ++ Templates:The Complete Guide」の章の第20章「スマートポインター」で詳しく説明されています。カバーするいくつかのトピック:
std::auto_ptr
は非推奨であり、誤って所有権を譲渡する可能性があるため、お勧めしません。- C ++ 11はブースト、使用の必要性を削除します std::unique_ptr
、std::shared_ptr
そしてstd::weak_ptr
Chris、Sergdev、およびLlyodによって提供された定義は正しいです。ただし、私の人生を単純にするために、より単純な定義を好みます。スマートポインターは、単純に->
and *
演算子をオーバーロードするクラスです。あなたのオブジェクトは、意味的にポインタのように見えますが、あなたはそれが参照カウント、自動破壊等を含む方法クーラーの事を、間に合わせることができ、どの手段
shared_ptr
とauto_ptr
、ほとんどの場合で十分ですが、小さな特異性の独自のセットと一緒に来ます。
スマートポインターは、「char *」のような通常の(型付き)ポインターに似ていますが、ポインター自体がスコープ外に出た場合、ポインターが指しているものも削除されます。"->"を使用することで、通常のポインタと同じように使用できますが、データへの実際のポインタが必要な場合は使用できません。そのためには、「&* ptr」を使用できます。
次の場合に便利です。
newで割り当てる必要があるが、そのスタック上の何かと同じ存続期間を持たせたいオブジェクト。オブジェクトがスマートポインターに割り当てられている場合、プログラムがその関数/ブロックを終了すると、オブジェクトは削除されます。
クラスのデータメンバー。オブジェクトが削除されると、デストラクタに特別なコードがなくても、所有されているすべてのデータも削除されます(デストラクタが仮想であることを確認する必要があります。これは、ほとんどの場合、適切です)。 。
次のような場合は、スマートポインタを使用したくない場合があります。
以下も参照してください。
ほとんどの種類のスマートポインターは、ポインターへのオブジェクトの破棄を処理します。オブジェクトを手動で破棄する必要がなくなったため、非常に便利です。
最も一般的に使用されるスマートポインターはstd::tr1::shared_ptr
(またはboost::shared_ptr
)であり、あまり一般的ではありませんが、std::auto_ptr
です。の常用をお勧めしますshared_ptr
。
shared_ptr
は非常に用途が広く、オブジェクトを「DLLの境界を越えて渡す」必要がある場合(libc
コードとDLLの間で異なるが使用される場合の一般的な悪夢のようなケース)を含む、さまざまな廃棄シナリオを扱います。
スマートポインターは、ポインターのように機能するオブジェクトですが、さらに、構築、破棄、コピー、移動、逆参照の制御を提供します。
独自のスマートポインターを実装することもできますが、多くのライブラリは、それぞれに異なる長所と短所を持つスマートポインターの実装も提供しています。
たとえば、Boostは次のスマートポインター実装を提供します。
shared_ptr<T>
T
参照カウントを使用してオブジェクトが不要になった時期を判断するためのポインタです。scoped_ptr<T>
範囲外になると自動的に削除されるポインタです。割り当てはできません。intrusive_ptr<T>
別の参照カウントポインタです。これは、よりも優れたパフォーマンスを提供しますが、独自の参照カウントメカニズムを提供するshared_ptr
タイプが必要T
です。weak_ptr<T>
は弱いポインタであり、shared_ptr
循環参照を回避するためにと連携して機能します。shared_array<T>
はに似てshared_ptr
いますが、の配列用ですT
。scoped_array<T>
はに似てscoped_ptr
いますが、の配列用ですT
。これらはそれぞれの1つの線形説明であり、必要に応じて使用できます。詳細と例については、Boostのドキュメントを参照してください。
さらに、C ++標準ライブラリは3つのスマートポインタを提供します。std::unique_ptr
ユニークな所有権のために、std::shared_ptr
所有権を共有してためstd::weak_ptr
。std::auto_ptr
C ++ 03に存在していましたが、現在は非推奨です。
scoped_ptr
ローカルで宣言const unique_ptr
されていない理由を説明してください-スコープを終了すると削除されます。
同様の回答のリンクは次のとおりです。http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html
スマートポインターは、通常のポインターのように動作、外観、および操作するオブジェクトですが、より多くの機能を提供します。C ++では、スマートポインターは、ポインターをカプセル化し、標準のポインター演算子をオーバーライドするテンプレートクラスとして実装されます。通常のポインターよりも多くの利点があります。これらは、nullポインターまたはヒープオブジェクトへのポインターとして初期化されることが保証されています。nullポインターによる間接参照がチェックされます。削除する必要はありません。オブジェクトへの最後のポインタがなくなると、オブジェクトは自動的に解放されます。これらのスマートポインターの1つの重要な問題は、通常のポインターとは異なり、継承を尊重しないことです。スマートポインタは、ポリモーフィックコードでは魅力的ではありません。以下は、スマートポインターの実装例です。
例:
template <class X>
class smart_pointer
{
public:
smart_pointer(); // makes a null pointer
smart_pointer(const X& x) // makes pointer to copy of x
X& operator *( );
const X& operator*( ) const;
X* operator->() const;
smart_pointer(const smart_pointer <X> &);
const smart_pointer <X> & operator =(const smart_pointer<X>&);
~smart_pointer();
private:
//...
};
このクラスは、タイプXのオブジェクトへのスマートポインターを実装します。オブジェクト自体はヒープに配置されます。使用方法は次のとおりです。
smart_pointer <employee> p= employee("Harris",1333);
他のオーバーロードされた演算子と同様に、pは通常のポインターのように動作します。
cout<<*p;
p->raise_salary(0.5);
http://en.wikipedia.org/wiki/Smart_pointer
コンピュータサイエンスでは、スマートポインタは、自動ガベージコレクションや境界チェックなどの追加機能を提供しながら、ポインタをシミュレートする抽象的なデータ型です。これらの追加機能は、効率を維持しながら、ポインターの誤用によって引き起こされるバグを減らすことを目的としています。スマートポインタは通常、メモリ管理の目的で、それらを指すオブジェクトを追跡します。ポインタの誤用はバグの主な原因です。ポインタを使用して作成されたプログラムで実行する必要がある定数の割り当て、割り当て解除、参照は、メモリリークが発生する可能性が非常に高くなります。スマートポインターは、リソースの割り当てを自動的に解除してメモリリークを防止しようとします。オブジェクトへのポインター(または一連のポインターの最後)が破棄されると、
TをこのチュートリアルのクラスとするC ++のポインターは、3つのタイプに分けることができます。
1)生のポインタ:
T a;
T * _ptr = &a;
それらは、メモリ内の場所へのメモリアドレスを保持します。プログラムを追跡するのが難しくなるので、注意して使用してください。
constデータまたはアドレスを持つポインター{逆方向読み取り}
T a ;
const T * ptr1 = &a ;
T const * ptr1 = &a ;
constであるデータ型Tへのポインター。つまり、ポインタを使用してデータ型を変更することはできません。すなわち*ptr1 = 19
; 動作しないでしょう。ただし、ポインタを移動することはできます。すなわちptr1++ , ptr1--
; などは動作します。逆方向に読む:constであるT型へのポインタ
T * const ptr2 ;
データ型Tへのconstポインター。つまり、ポインターを移動することはできませんが、ポインターが指す値を変更することはできます。つまり、機能し*ptr2 = 19
ますが、機能しptr2++ ; ptr2--
ません。逆方向に読む:型Tへのconstポインター
const T * const ptr3 ;
constデータ型Tへのconstポインター。つまり、ポインターを移動することも、データ型ポインターをポインターに変更することもできません。すなわち。ptr3-- ; ptr3++ ; *ptr3 = 19;
動作しないでしょう
3)スマートポインター:{ #include <memory>
}
共有ポインター:
T a ;
//shared_ptr<T> shptr(new T) ; not recommended but works
shared_ptr<T> shptr = make_shared<T>(); // faster + exception safe
std::cout << shptr.use_count() ; // 1 // gives the number of "
things " pointing to it.
T * temp = shptr.get(); // gives a pointer to object
// shared_pointer used like a regular pointer to call member functions
shptr->memFn();
(*shptr).memFn();
//
shptr.reset() ; // frees the object pointed to be the ptr
shptr = nullptr ; // frees the object
shptr = make_shared<T>() ; // frees the original object and points to new object
参照カウントを使用して実装され、ポインターが指すオブジェクトを指す「もの」の数を追跡します。このカウントが0になると、オブジェクトは自動的に削除されます。つまり、オブジェクトを指すすべてのshare_ptrがスコープから外れると、オブジェクトが削除されます。これにより、newを使用して割り当てたオブジェクトを削除しなければならないという頭痛がなくなります。
弱いポインタ: 共有ポインタを使用するときに発生する循環参照の処理に役立ちます。2つの共有ポインタが指す2つのオブジェクトがあり、相互に共有ポインタを指す内部共有ポインタがある場合、循環参照があり、オブジェクトはありません。共有ポインタが範囲外になったときに削除されます。これを解決するには、内部メンバーをshared_ptrからweak_ptrに変更します。注:弱いポインターが指す要素にアクセスするには、lock()を使用して、weak_ptrを返します。
T a ;
shared_ptr<T> shr = make_shared<T>() ;
weak_ptr<T> wk = shr ; // initialize a weak_ptr from a shared_ptr
wk.lock()->memFn() ; // use lock to get a shared_ptr
// ^^^ Can lead to exception if the shared ptr has gone out of scope
if(!wk.expired()) wk.lock()->memFn() ;
// Check if shared ptr has gone out of scope before access
参照:std :: weak_ptrが役立つのはいつですか?
ユニークなポインター: 独占的な所有権を持つ軽量のスマートポインター。ポインター間でオブジェクトを共有せずに、ポインターが一意のオブジェクトを指す場合に使用します。
unique_ptr<T> uptr(new T);
uptr->memFn();
//T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr
uptr.reset() ; // deletes the object pointed to by uptr
一意のptrが指すオブジェクトを変更するには、移動セマンティクスを使用します
unique_ptr<T> uptr1(new T);
unique_ptr<T> uptr2(new T);
uptr2 = std::move(uptr1);
// object pointed by uptr2 is deleted and
// object pointed by uptr1 is pointed to by uptr2
// uptr1 becomes null
参照:それらは本質的にconstポインターとして考えることができます。つまり、constであり、より良い構文では移動できないポインターです。
r-value reference : reference to a temporary object
l-value reference : reference to an object whose address can be obtained
const reference : reference to a data type which is const and cannot be modified
参照:https : //www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ この質問を指摘してくれたAndreに感謝します。
スマートポインターはクラスであり、通常のポインターのラッパーです。通常のポインターとは異なり、スマートポイントのライフサークルは参照カウント(スマートポインターオブジェクトが割り当てられた回数)に基づいています。したがって、スマートポインタが別のスマートポインタに割り当てられると、内部参照カウントにプラスが加算されます。また、オブジェクトがスコープから外れると、参照カウントからマイナスが引かれます。
自動ポインタは似ていますが、スマートポインタとはまったく異なります。これは、自動ポインターオブジェクトが変数のスコープから外れるたびにリソースの割り当てを解除する便利なクラスです。ある程度、(動的に割り当てられたメモリへの)ポインタが(コンパイル時に静的に割り当てられた)スタック変数と同様に機能するようにします。
既存の回答は適切ですが、スマートポインターが解決しようとしている問題に対する(完全な)回答ではない場合の対処方法については説明しません。
とりわけ、(他の回答でよく説明されている)スマートポインターの使用は、抽象クラスを関数の戻り値の型としてどのように使用するかに対する可能な解決策です。これはこの質問の複製としてマークされています。ただし、C ++で戻り値の型として抽象(または実際には任意の)基本クラスを指定したいかどうかを尋ねる最初の質問は、「本当にどういう意味ですか?」です。ブーストポインターコンテナーライブラリのドキュメントに、C ++での慣用的なオブジェクト指向プログラミング(および他の言語との違い)に関するよい議論があります。要約すると、C ++では所有権について考える必要があります。どのスマートポインターが役立ちますが、唯一の解決策ではないか、または常に完全な解決策(ポリモーフィックコピーを提供しない)であり、常にインターフェイスで公開する必要がある解決策ではありません(そして関数の戻りがひどいように聞こえます)インターフェースによく似ています)。たとえば、参照を返すだけで十分な場合があります。しかし、これらすべてのケース(スマートポインター、ポインターコンテナー、または単に参照を返す)では、戻り値を値から何らかの参照形式に変更しました。本当にコピーが必要な場合は、C ++のボイラープレート「イディオム」を追加するか、C ++の慣用的な(またはその他の)OOPを超えて、Adobe PolyやBoost.TypeErasureなどのライブラリを使用したより一般的なポリモーフィズムに移動する必要があります。。