【发布时间】:2017-10-02 16:51:54
【问题描述】:
我需要解释以下行为:
#include <iostream>
#include <memory>
#include <vector>
struct A {
std::string s = "foo";
std::weak_ptr<A> h;
std::shared_ptr<A> && getR() {
return std::move(h.lock());
}
std::shared_ptr<A> getL() {
return h.lock();
}
};
std::vector< std::shared_ptr<A> > storage;
std::vector< std::weak_ptr<A> > accountant;
void store(std::shared_ptr<A> && rr) {
std::cout << "store '" << rr->s << "' uses: " << rr.use_count() << std::endl;
storage.push_back(std::move(rr));
}
int main() {
// create keeper of A
auto keeper = std::make_shared<A>();
keeper->s = "bar";
// store weak_ptr-type handle with accountant
accountant.push_back(keeper);
// backref handle to A
keeper->h = accountant[0];
std::cout << "# case 0: manual 'move'" << std::endl;
{
store(std::move(accountant[0].lock()));
std::cout << "uses: " << keeper.use_count() << std::endl;
}
storage.clear();
std::cout << "# case 1: manual 'move' from internal" << std::endl;
{
store(std::move(keeper->h.lock()));
std::cout << "uses: " << keeper.use_count() << std::endl;
}
storage.clear();
std::cout << "# case 2: return copy from func" << std::endl;
{
store(keeper->getL());
std::cout << "uses: " << keeper.use_count() << std::endl;
}
storage.clear();
// all is well up to here.
std::cout << "# case 3: return rref from func" << std::endl;
{
store(keeper->getR());
std::cout << "uses: " << keeper.use_count() << std::endl;
std::cout << "storage[0]: " << storage[0].get() << " uses: " << storage[0].use_count() << " " << &storage[0] << std::endl;
std::cout << "keeper: " << keeper.get() << " uses: " << keeper.use_count() << " " << &keeper << std::endl;
}
storage.clear();
std::cout << "# after" << std::endl;
std::cout << "uses: " << keeper.use_count() << std::endl;
// all the A is gone!!!!
return 0;
}
输出:
# case 0: manual 'move'
store 'bar' uses: 2
uses: 2
# case 1: manual 'move' from internal
store 'bar' uses: 2
uses: 2
# case 2: return copy from func
store 'bar' uses: 2
uses: 2
# case 3: return rref from func
store 'bar' uses: 1
uses: 1
storage[0]: 0x2b49f7a0fc30 uses: 1 0x2b49f7a10ca0
keeper: 0x2b49f7a0fc30 uses: 1 0x7ffd3683be20
# after
uses: 0
ideone:http://ideone.com/smt7TX
这是一个为自己持有一个weak_ptr的类,所以它可以给自己一个shared_ptr-handles。它是真实代码中的资源类,shared_ptr 处理那些被传递的。现在为了减少复制 shared_ptrs 我遇到了我的 getHandle 函数(上面的 getR/getL )并希望它通过移动而不是复制来返回。在一个简短的测试 std::moving 中,weak_ptr::lock 的返回似乎没问题,但在最终代码中它把事情搞砸了。 与复制返回值相比,它似乎在移动它减少了 shared_ptr 的引用计数器 - 所以我最终有 2 个 shared_ptr 存在,但两者的 use_count() 都为 1。所以如果我使用 get() 得到的那个用完了范围 A 被破坏,我原来的 shared_ptr 仍然指向垃圾。 在示例代码中,您可以看到在案例 3 之后 - 我希望最后一个 cout 告诉我 use_count() 为 1,直到 keeper 被销毁。
现在在真正的代码中,我只是内联了 getL 的等价物,希望这可以防止多余的复制,但我无法克服不知道为什么这不能像我想象的那样工作的线索。
为什么案例 3 会减少引用计数? 那为什么case 0和1也不减少呢?
【问题讨论】:
-
在返回中使用
std::move通常是一种悲观。如果你想要一个纯右值,那么只需从函数中按值返回。 -
是的,你为什么要搬临时工?你不能让它们比现在更有价值。
-
"这是一个自身持有weak_ptr的类,因此它可以将shared_ptr-handles分配给自己。"为什么不从
std::enable_shared_from_this<A>派生并让它自动工作没有额外的努力,也没有参考周期? -
乔纳森和内森,是的...为什么确实...我应该知道的。 getHandle 函数一开始就更大,而且越来越归结起来——当它达到它的最终简短形式时,我一定是被蒙蔽了。还要感谢
std::enable_shared_from_this的提示,这正是我所需要的! -
奇怪的引用计数是因为你有未定义的行为,因为由于
std::move,你返回了一个悬空引用。它将右值转换为对空白空间的引用。
标签: c++ c++11 c++14 shared-ptr weak-ptr