web-dev-qa-db-ja.com

スマートポインタとは何ですか。いつ使用するべきですか。

スマートポインタとは何ですか。いつ使用するべきですか。

1616
Alex Reynolds

スマートポインタは、ポイントされているオブジェクトの有効期間を管理するために、 'raw'(または 'bare')C++ポインタをラップするクラスです。スマートポインタ型は1つではありませんが、すべて実用的な方法で生ポインタを抽象化しようとします。

スマートポインタは生のポインタよりも優先されるべきです。もしあなたがポインタを使う必要があると思うなら(最初にあなたが本当に doであるかどうかを考えてください)、生のポインタに関する多くの問題を軽減できるので、通常はスマートポインタを使いたいでしょう。そして記憶を漏らす。

生ポインタでは、プログラマはそれがもう役に立たなくなったときにオブジェクトを明示的に破棄しなければなりません。

// 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_ptrsは、オブジェクトの有効期間を特定のコードブロックに結び付けたい場合、または他のオブジェクトのメンバデータとして埋め込んだ場合はその他のオブジェクトの有効期間を指定する場合に便利です。オブジェクトは、それを含むコードのブロックが終了するまで、またはそれを含むオブジェクト自体が破棄されるまで存在します。

より複雑なスマートポインタポリシーでは、ポインタを参照カウントします。これにより、ポインタをコピーすることができます。オブジェクトへの最後の「参照」が破棄されると、そのオブジェクトは削除されます。このポリシーは 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を定義しました。


_ update _

この答えはかなり古く、Boostライブラリが提供していたスマートポインタであった当時の「良かった」ことを説明しています。 C++ 11以降、標準ライブラリは十分なスマートポインタ型を提供しているので、 std::unique_ptrstd::shared_ptr 、および std::weak_ptr の使用をお勧めします。

std::auto_ptr もあります。これはスコープ付きポインタに非常によく似ていますが、コピーする "特別な"危険な機能も持っている点が異なります。 最新の標準では推奨されていないので、使用しないでください。代わりに std::unique_ptr を使用してください。

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.
1780
Lloyd

これが現代のC++の最近の時代に対する簡単な答えです。

  • スマートポインタとは何ですか?
    ポインタのように値を使うことができるタイプですが、自動メモリ管理の追加機能を提供します。スマートポインタが使用されなくなると、それが指すメモリは割り当て解除されます( も参照)ウィキペディアでより詳細な定義 )。
  • いつ使うの?
    メモリの所有権の追跡、割り当てまたは割り当て解除を含むコード。スマートポインタを使用すると、これらのことを明示的に行う必要がなくなります。
  • しかし、どのスマートポインタを使うべきでしょうか。
    • 同じオブジェクトに対して複数の参照を保持するつもりがない場合は、 std::unique_ptr を使用してください。たとえば、スコープに入るときに割り当てられ、スコープから出るときに割り当て解除されるメモリへのポインタとして使用します。
    • オブジェクトを複数の場所から参照したい場合は std::shared_ptr を使用します。これらの参照がすべて削除されるまでオブジェクトの割り当てを解除しないでください。
    • オブジェクトを複数の場所から参照したい場合は、 std::weak_ptr を使用します - 無視して割り当てを解除してもかまいません(そのため、参照解除しようとするとオブジェクトが削除されます)。
    • 必要に応じて読み上げることができる特別な場合を除いて、boost::スマートポインタまたはstd::auto_ptrを使用しないでください。
  • ねえ、私はどちらを使うべきかと尋ねませんでした!
    ああ、でもあなたは本当にそれを認めたかったのです。
  • それでは、いつ私は通常のポインタを使うべきですか?
    主に、メモリの所有権を認識しないコード内これは通常、他の場所からポインタを取得し、割り当ても割り当て解除もせず、それらの実行を長引かせるポインタのコピーを格納しない関数にあります。
231
einpoklum

スマートポインタ は、追加機能を持つポインタ型です。自動メモリ割り当て解除、参照カウントなど.

小さなイントロがページにあります スマートポインタ - なに、なぜ、どれ?

単純なスマートポインタ型の1つは std::auto_ptr (C++標準の第20.4.5章)で、メモリがスコープ外になったときに自動的に解放され、例外がスローされたときの単純なポインタの使用よりも堅牢です。あまり柔軟ではありません。

もう1つの便利な型は boost::shared_ptr です。これは参照カウントを実装し、オブジェクトへの参照がなくなったときに自動的にメモリの割り当てを解除します。これはメモリリークを防ぐのに役立ち、 _ raii _ を実装するのに使いやすいです。

本の中で主題は深くカバーされています David Vandevoorde著、 "Nicolai M. Josuttis"による "C++テンプレート:完全ガイド" 、第20章スマートポインタ。いくつかのトピックが取り上げられました。

104
sergtk

Chris、Sergdev、Llyodによる定義は正しいです。私の人生を単純にするために、もっと単純な定義をお勧めします。スマートポインタは、->および*演算子をオーバーロードするクラスです。つまり、オブジェクトは意味的にはポインタのように見えますが、参照カウント、自動破棄など、よりクールなものにすることができます。ほとんどの場合、shared_ptrauto_ptrで十分ですが、独自の小さな特異点もあります。

38
Sridhar Iyer

スマートポインタは "char *"のような通常の(型付きの)ポインタに似ていますが、ポインタ自身が範囲外になった場合は、それが指すものも削除されます。 " - >"を使うことで普通のポインタと同じように使うことができますが、実際のデータへのポインタが必要な場合はそうではありません。そのためには、 "&* ptr"を使うことができます。

それは有用です:

  • Newを割り当てなければならないオブジェクトですが、そのスタック上の何かと同じ存続期間を持ちたいのです。オブジェクトがスマートポインタに割り当てられている場合、プログラムがその機能/ブロックを終了するとそれらは削除されます。

  • オブジェクトが削除されると、デストラクタ内に特別なコードがなくても、所有されているすべてのデータも削除されるようにします(デストラクタが仮想であることを確認する必要があります)。 。

あなたは ではないかもしれません 以下の場合にスマートポインタを使いたいでしょう:

  • ...ポインタは実際にデータを所有するべきではありません...つまり、あなたがデータを使用しているだけで、参照している関数を生き残って欲しいのです。
  • ...スマートポインタ自体は、ある時点で破壊されることはありません。動的に割り当てられているが明示的に削除されないオブジェクトのように、破壊されることのないメモリ内に配置したくはありません。
  • ... 2つのスマートポインタが同じデータを指す可能性があります。 (しかし、それを処理するより賢いポインタさえあります...それは 参照カウント と呼ばれます。)

また見なさい:

28
markets

ほとんどの種類のスマートポインタはあなたへのポインタへのオブジェクトの処分を扱います。手動でオブジェクトを破棄することをもう考える必要がないので、とても便利です。

最も一般的に使用されているスマートポインタはstd::tr1::shared_ptr(またはboost::shared_ptr)で、あまり一般的ではありませんがstd::auto_ptrです。私はshared_ptrの定期的な使用をお勧めします。

shared_ptrは非常に用途が広く、オブジェクトを「DLL境界を越えて渡す」必要がある場合など、さまざまな処理シナリオを扱います(コードとDLLの間で異なるlibcが使用される場合の一般的な悪夢の場合) 。

16

スマートポインタは、ポインタのように機能するオブジェクトですが、構築、破棄、コピー、移動、および参照解除を制御します。

自分自身のスマートポインタを実装することができますが、多くのライブラリはそれぞれ異なる長所と短所を持つスマートポインタの実装も提供します。

たとえば、 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の配列用です。

これらはそれぞれの線形説明にすぎず、必要に応じて使用できます。詳細および例については、Boostのドキュメントを参照してください。

さらに、C++標準ライブラリには3つのスマートポインタがあります。固有の所有権の場合はstd::unique_ptr、共有所有権の場合はstd::shared_ptr、およびstd::weak_ptrstd::auto_ptrはC++ 03で存在していましたが、現在は非推奨です。

16
Saqlain

これは同様の答えのためのリンクです: 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);
10
Santosh

http://en.wikipedia.org/wiki/Smart_pointer

コンピュータサイエンスでは、スマートポインタは、自動ガベージコレクションや境界チェックなどの追加機能を提供しながらポインタをシミュレートする抽象データ型です。これらの追加機能は、効率を維持しながらポインタの誤用によって引き起こされるバグを減らすことを目的としています。スマートポインタは通常、メモリ管理の目的でそれらを指すオブジェクトを追跡します。ポインタの誤用は、主なバグの原因となっています。ポインタを使って書かれたプログラムで実行しなければならない定数の割り当て、割り当て解除、参照は、メモリリークを起こす可能性が非常に高いです。スマートポインタは、リソースの割り当て解除を自動的に行うことでメモリリークを防ごうとします。たとえば、オブジェクトへのポインタ(または一連のポインタの最後のポインタ)が破棄された場合など.

8
Jorge Ferreira

このチュートリアルでは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つのオブジェクトがあり、互いに共有ポインタを指す内部共有ポインタがある場合共有ポインタが範囲外になっても、referenceおよびオブジェクトは削除されません。これを解決するには、内部メンバーを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が指すオブジェクトを変更するには、moveセマンティクスを使用します。

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であり、より良い構文では移動できないポインタのようなものです。

参照してください: C + +のポインタ変数と参照変数の違いは何ですか?

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に感謝します。

5
nnrales

スマートポインタはクラス、通常のポインタのラッパーです。通常のポインタとは異なり、スマートポイントのライフサークルは参照カウント(スマートポインタオブジェクトが割り当てられた回数)に基づいています。したがって、スマートポインタが別のポインタに割り当てられるたびに、内部参照カウントはプラスになります。そして、オブジェクトが範囲外に出るたびに、参照カウントはマイナスマイナスになります。

自動ポインタは、外観は似ていますが、スマートポインタとはまったく異なります。自動ポインタオブジェクトが変数のスコープ外に出るたびにリソースの割り当てを解除する便利なクラスです。ある程度、(動的に割り当てられたメモリへの)ポインタを(コンパイル時に静的に割り当てられた)スタック変数と同様に機能させます。

3
Trombe

スマートポインタは、メモリ割り当て解除、リソース共有、転送について心配する必要がない場所です。

Javaで割り当てが機能するのと同じように、これらのポインタを非常にうまく使用できます。 Java Garbage Collectorではトリックを行いますが、Smart PointersではトリックはDestructorsによって行われます。

2
Daksh

既存の答えは良いですが、スマートポインタがあなたが解決しようとしている問題への(完全な)答えではないときに何をすべきかをカバーしません。

スマートポインタを使用することは、 の解決策の1つです(他の回答で詳しく説明されています)。関数の戻り値の型として抽象クラスをどのように使用しますか。 これはこの質問の複製としてマークされています。しかし、C++で戻り値の型として抽象(または実際には任意の)基本クラスを指定したいかどうかを尋ねる最初の質問は、「どういう意味ですか?」です。 boostポインタコンテナライブラリ のドキュメントには、C++での慣用的なオブジェクト指向プログラミング(および他の言語とはどのように異なるのか)についての(さらなる参照との)良好な議論があります。まとめると、C++では所有権について考える必要があります。どのスマートポインタがあなたを手助けしますが、唯一の解決策ではなく、常に完全な解決策でもあり(多形コピーを与えません)、常にあなたがあなたのインターフェースで公開したい解決策ではありません。インターフェースのようなものです)。たとえば、参照を返すだけで十分かもしれません。しかし、これらすべての場合(スマートポインタ、ポインタコンテナ、または単に参照を返す)では、戻り値を value から何らかの形式の reference に変更しました。本当にコピーが必要な場合は、定型句 "idiom"を追加するか、C++の慣用句(またはそれ以外の場合)OOPを超えて Adob​​e PolyBoostなどのライブラリを使用します。 TypeErasure

1
da77a