web-dev-qa-db-ja.com

shared_ptrを使用する例?

こんにちは 同じベクトル配列にさまざまな種類のオブジェクトを挿入する方法 について質問しました。その質問のコードは

 gate* G[1000];
G[0] = new ANDgate() ;
G[1] = new ORgate;
//gate is a class inherited by ANDgate and ORgate classes
class gate
{
 .....
 ......
 virtual void Run()
   {   //A virtual function
   }
};
class ANDgate :public gate 
  {.....
   .......
   void Run()
   {
    //AND version of Run
   }  

};
 class ORgate :public gate 
  {.....
   .......
   void Run()
   {
    //OR version of Run
   }  

};      
//Running the simulator using overloading concept
 for(...;...;..)
 {
  G[i]->Run() ;  //will run perfectly the right Run for the right Gate type
 } 

そして、私はベクトルを使用したかったので、誰かが私がそれをするべきだと書いた:

std::vector<gate*> G;
G.Push_back(new ANDgate); 
G.Push_back(new ORgate);
for(unsigned i=0;i<G.size();++i)
{
  G[i]->Run();
}

しかし、その後、彼と他の多くの人が私がより良い使用を提案しました ブーストポインタコンテナ
またはshared_ptr。過去3時間、このトピックについて読んでいますが、ドキュメントはかなり進歩しているようです。 **** shared_ptrの使用法とshared_ptrの使用を提案した理由の小さなコード例を教えてください。 ptr_vectorptr_listptr_deque **などの他のタイプもあります**

編集1:私も含まれているコード例を読んだ:

typedef boost::shared_ptr<Foo> FooPtr;
.......
int main()
{
  std::vector<FooPtr>         foo_vector;
........
FooPtr foo_ptr( new Foo( 2 ) );
  foo_vector.Push_back( foo_ptr );
...........
}

そして、私は構文を理解していません!

81
Ahmed

vector of shared_ptrを使用すると、ベクトルをウォークスルーして各要素でdeleteを呼び出すのを忘れたため、メモリリークの可能性がなくなります。サンプルの少しずつ修正されたバージョンを行ごとに見ていきましょう。

typedef boost::shared_ptr<gate> gate_ptr;

共有ポインタータイプのエイリアスを作成します。これにより、std::vector<boost::shared_ptr<gate> >と入力し、閉じ記号より大きい記号の間のスペースを忘れることによるC++言語のさを回避できます。

    std::vector<gate_ptr> vec;

boost::shared_ptr<gate>オブジェクトの空のベクターを作成します。

    gate_ptr ptr(new ANDgate);

新しいANDgateインスタンスを割り当てて、shared_ptrに保存します。これを個別に行う理由は、操作がスローされた場合に発生する可能性がある問題を防ぐためです。この例ではこれは不可能です。 Boost shared_ptr "Best Practices" は、一時的ではなく独立したオブジェクトに割り当てるのがベストプラクティスである理由を説明します

    vec.Push_back(ptr);

これにより、ベクターに新しい共有ポインターが作成され、ptrがコピーされます。 shared_ptrの内部の参照カウントにより、ptr内の割り当てられたオブジェクトがベクトルに安全に転送されます。

説明されていないのは、shared_ptr<gate>のデストラクタによって、割り当てられたメモリが確実に削除されるということです。これは、メモリリークが回避される場所です。 std::vector<T>のデストラクタは、Tのデストラクタがベクトルに格納されているすべての要素に対して確実に呼び出されるようにします。ただし、ポインタのデストラクタ(たとえば、gate*は、割り当てたメモリを削除しません。これは、shared_ptrまたはptr_vectorを使用して回避しようとしているものです。

114
D.Shawley

shared_ptrの重要なことの1つは、everのみを次の構文で構築することです。

shared_ptr<Type>(new Type(...));

この方法では、Typeへの「実際の」ポインターはスコープに対して匿名であり、共有ポインターによってonlyを保持します。したがって、この「実際の」ポインターを誤って使用することはできません。言い換えれば、これを絶対にしないでください:

Type* t_ptr = new Type(...);
shared_ptr<Type> t_sptr ptrT(t_ptr);
//t_ptr is still hanging around!  Don't use it!

これは機能しますが、共有ポインターの外側にある関数にType*ポインター(t_ptr)があります。 t_ptrをどこでも使用するのは危険です。なぜなら、それを保持している共有ポインターがいつ破壊するかわからないため、セグメンテーション違反になるからです。

他のクラスから返されるポインターについても同様です。書いていないクラスがポインタを渡した場合、それをshared_ptrに入れるだけでは一般に安全ではありません。あなたがsureでない限り、クラスがそのオブジェクトを使用しなくなっているわけではありません。 shared_ptrに入れて、スコープから外れると、クラスがそれを必要とする可能性があるときにオブジェクトが解放されるためです。

40
Ken Simon

私の意見では、スマートポインターの使用を学ぶことは、有能なC++プログラマーになるための最も重要なステップの1つです。ご存知のように、ある時点でオブジェクトを新規作成するときはいつでも、それを削除したいと思います。

発生する問題の1つは、例外を除き、可能なすべての実行パスでオブジェクトが常に1回だけ解放されるようにすることは非常に難しいことです。

これがRAIIの理由です: http://en.wikipedia.org/wiki/RAII

オブジェクトがすべての実行パスで必ず1回削除されるようにする目的でヘルパークラスを作成します。

このようなクラスの例:std :: auto_ptr

ただし、オブジェクトを他のユーザーと共有したい場合があります。誰ももう使用していない場合にのみ削除する必要があります。

その参照カウント戦略を支援するために、戦略を開発しましたが、addrefを覚えて、refを手動で解放する必要があります。本質的に、これは新規/削除と同じ問題です。

Boostがboost :: shared_ptrを開発したのはそのためです。これはスマートポインターを参照する参照であるため、オブジェクトを共有し、意図せずメモリをリークすることはありません。

C++ tr1の追加により、これはc ++標準にも追加されましたが、その名前はstd :: tr1 :: shared_ptr <>です。

可能であれば、標準の共有ポインターを使用することをお勧めします。 ptr_list、ptr_dequeueなどは、ポインタータイプ用のIIRC専用コンテナーです。今のところ無視します。

したがって、あなたの例から始めることができます:

std::vector<gate*> G; 
G.Push_back(new ANDgate);  
G.Push_back(new ORgate); 
for(unsigned i=0;i<G.size();++i) 
{ 
  G[i]->Run(); 
} 

ここでの問題は、Gがスコープから出るたびに、Gに追加された2つのオブジェクトをリークすることです。std:: tr1 :: shared_ptrを使用するように書き換えましょう。

// Remember to include <memory> for shared_ptr
// First do an alias for std::tr1::shared_ptr<gate> so we don't have to 
// type that in every place. Call it gate_ptr. This is what typedef does.
typedef std::tr1::shared_ptr<gate> gate_ptr;    
// gate_ptr is now our "smart" pointer. So let's make a vector out of it.
std::vector<gate_ptr> G; 
// these smart_ptrs can't be implicitly created from gate* we have to be explicit about it
// gate_ptr (new ANDgate), it's a good thing:
G.Push_back(gate_ptr (new ANDgate));  
G.Push_back(gate_ptr (new ORgate)); 
for(unsigned i=0;i<G.size();++i) 
{ 
   G[i]->Run(); 
} 

Gが範囲外になると、メモリは自動的に回収されます。

私のチームの新参者を悩ませたエクササイズとして、彼らに彼ら自身のスマートポインタクラスを書くように頼むことです。その後、クラスをすぐに破棄し、二度と使用しないでください。スマートポインターが内部でどのように機能するかについての重要な知識を取得したことを願っています。魔法はありません。

ブーストのドキュメントには、かなり良い開始例があります: shared_ptrの例 (実際にはスマートポインターのベクトルに関するもの)または shared_ptr doc かなりよく: スマートポインターの説明

(可能な限り少ない単語で)ptr_vectorの背後にある考え方は、保存されたポインターの背後にあるメモリの割り当て解除を処理するということです。例のようにポインターのベクトルがあるとします。アプリケーションを終了するか、ベクターが定義されているスコープを離れるときは、自分でクリーンアップする必要があります(動的にANDgateとORgateを割り当てました)が、ベクターがポインターを格納しているため、ベクターをクリアするだけではできません実際のオブジェクトではありません(破棄されませんが、含まれるもの)。

 // if you just do
 G.clear() // will clear the vector but you'll be left with 2 memory leaks
 ...
// to properly clean the vector and the objects behind it
for (std::vector<gate*>::iterator it = G.begin(); it != G.end(); it++)
{
  delete (*it);
}

boost :: ptr_vector <>は上記を処理します。つまり、格納するポインターの背後にあるメモリの割り当てを解除します。

2
celavek

Boostでできること>

std::vector<boost::any> vecobj;
    boost::shared_ptr<string> sharedString1(new string("abcdxyz!"));    
    boost::shared_ptr<int> sharedint1(new int(10));
    vecobj.Push_back(sharedString1);
    vecobj.Push_back(sharedint1);

>ベクターコンテナに異なるオブジェクトタイプを挿入するため。アクセスするには、dynamic_castのように機能するany_castを使用する必要があります。

2
user1808932
#include <memory>
#include <iostream>

class SharedMemory {
    public: 
        SharedMemory(int* x):_capture(x){}
        int* get() { return (_capture.get()); }
    protected:
        std::shared_ptr<int> _capture;
};

int main(int , char**){
    SharedMemory *_obj1= new SharedMemory(new int(10));
    SharedMemory *_obj2 = new SharedMemory(*_obj1);
    std::cout << " _obj1: " << *_obj1->get() << " _obj2: " << *_obj2->get()
    << std::endl;
    delete _obj2;

    std::cout << " _obj1: " << *_obj1->get() << std::endl;
    delete _obj1;
    std::cout << " done " << std::endl;
}

これは、動作中のshared_ptrの例です。 _obj2は削除されましたが、ポインターはまだ有効です。出力は、。/ test _obj1:10 _obj2:10 _obj2:10 done

1
Syed Raihan

異なるオブジェクトを同じコンテナに追加する最良の方法は、make_shared、vector、およびrangeベースのループを使用することです。そうすれば、きれいできれいな「読みやすい」コードが得られます!

typedef std::shared_ptr<gate> Ptr   
vector<Ptr> myConatiner; 
auto andGate = std::make_shared<ANDgate>();
myConatiner.Push_back(andGate );
auto orGate= std::make_shared<ORgate>();
myConatiner.Push_back(orGate);

for (auto& element : myConatiner)
    element->run();
0
Hooman