shared_ptrを使用する例?


82

こんにちは私は今日、同じベクトル配列に異なるタイプのオブジェクトを挿入する方法について質問しました、そしてその質問の私のコードは

 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();
}

しかし、彼と他の多くの人は、Boostポインタコンテナ
またはを使用する方がよいと提案しましたshared_ptr。私は過去3時間このトピックについて読んでいますが、ドキュメントはかなり進んでいるようです。****缶誰も私の小さなコード例与えるshared_ptr使用をし、彼らが使用して提案理由shared_ptr。また、のような他の種類がありますptr_vectorptr_listptr_deque**

Edit1:私も以下を含むコード例を読みました:

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 );
...........
}

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


2
どの構文がわかりませんか?の最初の行はmainFoo;と呼ばれる型への共有ポインタを含むことができるベクトルを作成します。2つ目は、Foousingnewと、それを管理するための共有ポインターを作成します。3つ目は、共有ポインターのコピーをベクトルに配置します。
マイクシーモア

回答:


116

vectorofを使用するとshared_ptr、ベクトルをウォークdeleteして各要素を呼び出すのを忘れたため、メモリリークの可能性がなくなります。例のわずかに変更されたバージョンを1行ずつ見ていきましょう。

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。これを個別に行う理由は、操作がスローされた場合に発生する可能性のある問題を防ぐためです。この例では、これは不可能です。ブーストshared_ptr「ベストプラクティス」は、ある理由を説明するベストプラクティスの代わりに一時的に自立オブジェクトに割り当てます。

    vec.push_back(ptr);

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

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


1
それは詳細でした:)。私の質問は、コードgate_ptr ptr(new ANDgate);の3行目に関するものです。共有ポインタゲートタイプのptrと、中括弧の間に新しいANDゲートを送信したことは、私にはあまり馴染みがありません。それは紛らわしいです。
アーメド

6
@Ahmed:値5でint x(5);初期化xするように書くのと同じように、式全体は変数の初期化です。この場合、ANDgate;を作成するnew-expressionの値で初期化されています。new-expressionの値は、新しいオブジェクトへのポインタです。
マイクシーモア

42

私は約重要なことの1つ追加されますshared_ptrのが唯一のことです今までに次の構文でそれらを構築します:

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

このように、への「実際の」ポインタTypeはスコープに対して匿名であり、共有ポインタによってのみ保持されます。したがって、この「実際の」ポインタを誤って使用することは不可能です。言い換えれば、これは絶対に行わないでください。

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。クラスがそのオブジェクトを使用していないことが確実でない限り、そうではありません。に入れてshared_ptrスコープから外れると、クラスがまだ必要としているときにオブジェクトが解放されるためです。


8
ケンが言ったことはすべて良くて真実ですが、その形式がより効率的であるという理由だけで、今それを呼び出すための好ましい方法は、auto t_ptr = make_shared<Type>(...);または同等shared_ptr<Type> t_ptr = make_shared<Type>(...);であると信じています。
Jason Sydes 2013年

@KenSimonは、カンマがあると想定している,t_sptrptrTではshared_ptr<Type> t_sptr ptrT(t_ptr);
Allanqunzi 2015年

サンプルコードのあいまいさは別として、良い警告ですが、最初のフォームは非常にクリーンであり、おそらくもっと重要なことですが、スマートポインターを使用している人なら誰でも、危険な生を避けるために正確に存在することを知っているはずです。ポインタが浮かんでいます。最後の段落は興味深いものです。ありがたいことに、生のタイプまたは不明確なタイプのポイントを使用するように強制するライブラリはまだ使用していませんが、いつかは発生すると確信しています。
underscore_d 2016年

20

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

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

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

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

このようなクラスの例は次のとおりです。std:: auto_ptr

しかし、オブジェクトを他の人と共有したい場合もあります。誰も使用しなくなった場合にのみ削除する必要があります。

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

これが、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がスコープ外になると、メモリは自動的に再利用されます。

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


私のインストラクターは私に自分のクラスを書くことについて同様のアドバイスをくれたので、私はそれを確実に試みます。TY。
アーメド

あなたはすべてのゲートを実行するためにイテレータを使用する必要がありますfor( auto itt = G.begin(); itt != G.end(); ++itt ){ itt->Run(); }
ギヨームマッセ

1
または、C ++の新しい「foreach」
1つのメタ

2

ブーストのドキュメントは、かなり良い開始例を提供します: shared_ptrの例(実際にはスマートポインターのベクトルに関するものです)または shared_ptr doc Johannes Schaubによる次の回答は、ブーストのスマートポインターを非常によく説明しています: スマートポインターの説明

(可能な限り少ない単語で)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 <>は上記を処理します。つまり、格納するポインタの背後にあるメモリの割り当てを解除します。


shared_ptrは、スマートポインターです。たとえば、ポインター型にAIを追加する、プレーンポインターの光沢のある「ラッパー」です。ptr_vectorは、ポインターのスマートコンテナーであり、ポインターのコンテナーの「ラッパー」です。
celavek 2010

したがって、ptr_vectorは通常のベクトルの一種の置換ですか?
アーメド

@Ahmedあなたはそれをそのように考えることができると思います。
celavek 2010

2

ブーストを通してあなたはそれをすることができます>

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を使用する必要がありますが、必要に応じて機能することを期待しています。


1
#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完了


0

異なるオブジェクトを同じコンテナーに追加する最良の方法は、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();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.