简体   繁体   English

std :: move_ptr :: lock的返回值弄乱了shared_ptr的引用计数?

[英]std::move return value of weak_ptr::lock messing up reference count of shared_ptr?

i need an explanation of the following behaviour: 我需要以下行为的解释:

#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;
}

output: 输出:

# 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 ideone: http ://ideone.com/smt7TX

This is a class holding a weak_ptr to itself, so it can give out shared_ptr-handles to itself. 这是一个对自身持有weak_ptr的类,因此它可以为其自身提供shared_ptr-handles。 Its a resource-class in the real code, and shared_ptr handles to those get passed around. 它是实际代码中的资源类,并且shared_ptr处理传递给它们的那些。 Now in an effort to reduce copying shared_ptrs i came across my getHandle function (getR/getL in the above) and wanted it to return by moving instead of copying. 现在,为了减少复制shared_ptrs,我遇到了我的getHandle函数(上面的getR / getL),希望它通过移动而不是复制来返回。 In a short test std::moving the return of weak_ptr::lock seemed ok, but in the final code it messed things up bad. 在简短的测试中,std :: move返回weak_ptr :: lock的返回值似乎不错,但是在最终代码中,它使事情变得很糟。 In comparison to copying the return-value it seems moving it reduces the shared_ptr's reference counter - so i end up with 2 shared_ptrs in existence but both having a use_count() of 1. so if the one i got using get() goes out of scope the A gets destroyed and my original shared_ptr which is still around points to garbage. 与复制返回值相比,移动它似乎减少了shared_ptr的引用计数器-因此我最终获得了2个shared_ptrs,但两者的use_count()均为1。因此,如果我使用get()获得的那一个不再使用范围A被销毁,而我的原始shared_ptr仍然指向垃圾点。 In the example code you can see that after case 3 - i would have expected the last cout to tell me a use_count() of 1 until keeper is destroyed. 在示例代码中,您可以看到在情况3之后-我期望最后一个提示告诉我use_count()为1,直到销毁了keeper。

Now in the real code i just inlined the equivalent of getL in the hopes that this will prevent the superflous copying, but i can't get over not having a clue why this doesn't work as i thought it would. 现在,在实际代码中,我只是内联了getL的等效项,希望这将防止多余的复制,但是我无法克服一个不知道为什么它不起作用的线索。

Why does case 3 reduce the reference count? 为什么情况3会减少引用计数? And then why don't case 0 and 1 also reduce it? 然后为什么不将0和1也减呢?

You have a bug here: 您在这里有一个错误:

std::shared_ptr<A> && getR() {
    return std::move(h.lock());
}

This creates a temporary shared_ptr which is local to the function then returns a reference to it. 这将创建一个临时的shared_ptr ,该值在函数本地,然后返回对该函数的引用。 That is a dangling reference to an object that no longer exists. 那是对不再存在的对象的悬挂引用。 Just return by value as getL does (I don't know why you've called it getL ... if that refers to an lvalue it's wrong, it returns an rvalue). 就像getL一样按值返回(我不知道您为什么称它为getL ...如果它引用的是左值,那是错误的,它将返回一个右值)。

You are misusing std::move in a misguided attempt to improve performance, but simply returning the object is simpler, safer, and allows the compiler to optimise it far more effectively. 您滥用std::move mo进行了误导的尝试以提高性能,但是仅返回对象就更简单,更安全,并且允许编译器更有效地对其进行优化。 Without the std::move there won't be any copy or move, the compiler will elide the temporary completely, see What are copy elision and return value optimization? 如果没有std::move ,将不会有任何复制移动,编译器将完全消除临时文件,请参见什么是复制省略和返回值优化?

These other moves are also redundant (although not actually harmful here): 这些其他步骤也是多余的(尽管在这里实际上并没有害处):

    store(std::move(accountant[0].lock()));

    store(std::move(keeper->h.lock()));

In both cases you're trying to move something that is already an rvalue, which is pointless. 在这两种情况下,您都试图移动已经是右值的东西,这毫无意义。

Also you have reimplemented std::enable_shared_from_this , poorly. 另外,您std::enable_shared_from_this好地重新实现了std::enable_shared_from_this Get rid of your weak_ptr member and your backref and just do: 摆脱您的weak_ptr成员和backref,然后执行以下操作:

struct A : std::enable_shared_from_this<A> {
    std::string s = "foo";
};

And then call keeper->shared_from_this() instead of keeper->getL() . 然后调用keeper->shared_from_this()而不是keeper->getL() You'll notice that shared_from_this() returns by value, not by reference, to avoid the bug in your getR() function. 您会注意到, shared_from_this()按值而不是按引用返回的,以避免getR()函数中的错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM