shared_ptr 的 use_count() 在从向量中检索时返回意外值
Unexpected value returned by use_count() of shared_ptr while retrieving from vector
当使用 std::vector
的 iterator
取消引用打印共享指针时,下面的程序输出意外的 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()
来自 tmpPtr
。 push_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
投射。
当使用 std::vector
的 iterator
取消引用打印共享指针时,下面的程序输出意外的 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()
来自 tmpPtr
。 push_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
投射。