2017-08-02 2 views
-2

から取得中に共有ポインタがstd::vectoriteratorデリファレンスを使用して印刷されたときに以下の予想外use_count()値を出力しているプログラム:予期しない値()のshared_ptrのベクトル

#include<iostream> 
#include<memory> 
#include<vector> 

class A; 

typedef std::shared_ptr<A>   sharedPtr; 
typedef std::vector<sharedPtr>  sharedPtrVect; 
typedef sharedPtrVect::const_iterator vectItr; 

class A 
{ 
    public: 
    A(int inp): m_Val(inp) { /*std::cout << "*** A ctor called: " << m_Val << " ***" <<std::endl;*/ } 
    ~A() { /*std::cout << "### A dtor called: " << m_Val << " ###" <<std::endl; */} 

    int getVal() const { return m_Val; } 

    private: 
    int m_Val; 
}; 

int main() 
{ 
    sharedPtrVect myVect1, myVect2; 
    vectItr myVectItr; 
    std::shared_ptr<A> tmpPtr; 

    for(int i = 1 ; i <= 5 ; i++) { 
    std::cout << "Pushed back: " << i << std::endl; 
    tmpPtr = std::make_shared<A>(i); 
    myVect1.push_back(tmpPtr); 
    } 

    myVectItr = myVect1.begin(); 

    for( ; myVectItr != myVect1.end() ; ++myVectItr) { 
    std::cout << "-----------------------------" << std::endl; 
    std::cout << "Element number: " << (*myVectItr).get()->getVal() << std::endl; 
    std::cout << "Element use count: " << (*myVectItr).use_count() << std::endl; 
    std::cout << "-----------------------------" << std::endl; 
    } 

    return 0; 
} 

上記のコードの出力次のとおりです。

Pushed back: 1 
Pushed back: 2 
Pushed back: 3 
Pushed back: 4 
Pushed back: 5 
----------------------------- 
Element number: 1 
Element use count: 1 
----------------------------- 
----------------------------- 
Element number: 2 
Element use count: 1 
----------------------------- 
----------------------------- 
Element number: 3 
Element use count: 1 
----------------------------- 
----------------------------- 
Element number: 4 
Element use count: 1 
----------------------------- 
----------------------------- 
Element number: 5 
Element use count: 2  //I am not sure why or how this is 2? 
----------------------------- 

私は最後のベクトル要素のためuse_count()がどのように理解していない2.それは他の人のように1ではないでしょうか?私はベクトルの最後の要素に格納されている共有ポインタのコピーを作成していません。 私はここで何が欠けていますか?

編集:私はC++ 98で良い経験をしていますが、C++ 11では経験が少ないです。

答えて

7

他人のように1ではありませんか?私はベクトルの最後の要素に格納されている共有ポインタのコピーを作成していません。私はここで何が欠けていますか?

ただし、です。あなたはpush_back()からtmpPtrまでです。 push_back()は、代わりに移動するよう指示しない限り、その引数のコピーをベクトルに格納します。 (!それについての詳細は後ほど)

したがって、何がすべてのために起こりますが、最後の要素はこれです:

  • tmpPtrは、共有リソース
  • への参照のみあなたpush_back()コピーを保持しているので、コピー-constructor of shared_ptrは、使用カウントを2に増やします。
  • 次に、次の要素をtmpPtrに割り当てて、前の要素のリソースへの参照を解放し、その使用回数を減らします。

もちろん、ループの最後の反復で後続の割り当てはありません。したがって、印刷時点では、tmpPtrは依然として有効範囲にあり、割り当てられた最後のリソースへの参照が保持されます。したがって、最後の要素に対する1より高いrefcount。これは完全に私に期待されるようです。 ;)

期待した結果を確認するには、コピーした後、印刷する前にtmpPtrを破棄するか、最初からそのコピーを避ける必要があります。前者の場合は、がin the commentsと指摘されているので、その宣言をforループに移動することによって行うことができます。

ただし、明らかに後者が優れています。どうすればいいの? C++ 11では、代わりにに移動させています。したがって、emplace_back(std::move(tmpPtr))になります。moveはrvalueにキャストされ、vector要素の移動コンストラクタを呼び出します。これにより、vectorに移動したときにtmpPtrの参照が解放され、使用回数が常に1になることが保証されます。tmpPtr(移動先オブジェクト)は有効であるが不特定の状態になります。つまり、 -に。

(注:push_back()は同じことを達成するが、それは他の状況では、より効率的なので、それはより良いデフォルトだと、私は一般的に、emplace_back()可能な限りを使用して好む)

もちろん、あなたがして、両方を組み合わせることができますforループの範囲内でtmpPtrを宣言すると、がそこから移動します。 しかし ...あなたも全くtmpPtrは必要ありません!それは有用な目的を果たしていないようです。だから、あなたはそれを直接使うことはできず、その代わりに直接emplace_back()の結果をmake_shared()とします。戻り値はの値のになるため、暗黙的にvectorに移動されます。 std::moveによるキャストは必要ありません。

+2

また、良いコーディングスタイルとして、 'tmpPtr'はループの中で宣言されるべきです、なぜならそれはそれが使用される唯一の場所だからです。 – SirGuy

+1

@SirGuyはい、可能な限り狭い範囲で宣言します。あなたが何も存在する必要がないと宣言している場合を除きます。 :Pとにかく、何かに必要なものがあれば、 'auto'で宣言して、RHSの' make_shared'式から推論する方が簡単です。 –

+1

詳細な説明のために@underscore_dをありがとうございます。今あなたの説明の後、私は自分自身でこれを持っているはずです!しかし、詳細はすべてです。それはC++の美しさです、私は信じています:) – Upayan

関連する問題