shared_ptr 的 use_count() 在从向量中检索时返回意外值

Unexpected value returned by use_count() of shared_ptr while retrieving from vector

当使用 std::vectoriterator 取消引用打印共享指针时,下面的程序输出意外的 use_count() 值:

#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?
-----------------------------

我不明白最后一个vector元素的use_count()怎么是2,不应该像其他的一样是1吗?我没有创建存储在向量的最后一个元素中的共享指针的任何副本。 我在这里错过了什么?

编辑:我在 C++98 方面经验丰富,但在 C++11 方面经验较少。

Shouldn't it be 1 like others? I am not creating any copies of the shared pointer stored in the last element of the vector. What am I missing here?

但是您正在创建副本。你 push_back() 来自 tmpPtrpush_back() 将其参数的副本放入向量中,除非您告诉它移动。 (稍后会详细介绍!)

因此,除最后一个元素之外的所有元素都会发生以下情况:

  • tmpPtr 持有对共享资源的唯一引用
  • push_back() 一个副本,所以 shared_ptr 的复制构造函数将使用计数增加到 2
  • 然后您将下一个元素分配给 tmpPtr,释放对前一个元素资源的引用,从而减少其使用次数。

但是,当然,在循环的最后一次迭代中没有后续赋值。因此,在打印时,tmpPtr 仍在范围内,并且它保留了对最后分配的资源的引用。因此最后一个元素的 1-higher 引用计数。这似乎完全符合我的预期。 ;)

要查看您预期的结果,您需要在复制后但打印前销毁 tmpPtr,或者干脆首先避免复制它。前者可以通过将其声明移动到 for 循环中来完成,如 SirGuy pointed out .

不过,显然后者更胜一筹。我们该怎么做?好吧,C++11 允许我们 move。因此,您可以 emplace_back( std::move(tmpPtr) ),其中 move 将强制转换为右值,从而调用 vector 元素的移动构造函数。这将导致 tmpPtr 在移入 vector 时释放其引用,有效地确保使用计数始终为 1。这使 tmpPtr(就像任何移出的对象一样)处于有效状态-但未指定状态,即仅对重新分配有用。

(注意:push_back() 会达到同样的目的,但我通常更喜欢尽可能使用 emplace_back(),因为它在其他情况下效率更高,所以它是更好的默认值。)

当然,你可以将这两者结合起来:在for循环的范围内声明tmpPtr从它移动。 然而...你根本不需要tmpPtr!它似乎没有任何用处。因此,您可以不使用它,而是直接 emplace_back() make_shared() 的结果。因为它的 return 值将是一个 rvalue,它会被隐式地移动到 vector;不需要 std::move 投射。