簡體   English   中英

std :: move_ptr :: lock的返回值弄亂了shared_ptr的引用計數?

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

我需要以下行為的解釋:

#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 :: move返回weak_ptr :: lock的返回值似乎不錯,但是在最終代碼中,它使事情變得很糟。 與復制返回值相比,移動它似乎減少了shared_ptr的引用計數器-因此我最終獲得了2個shared_ptrs,但兩者的use_count()均為1。因此,如果我使用get()獲得的那一個不再使用范圍A被銷毀,而我的原始shared_ptr仍然指向垃圾點。 在示例代碼中,您可以看到在情況3之后-我期望最后一個提示告訴我use_count()為1,直到銷毀了keeper。

現在,在實際代碼中,我只是內聯了getL的等效項,希望這將防止多余的復制,但是我無法克服一個不知道為什么它不起作用的線索。

為什么情況3會減少引用計數? 然后為什么不將0和1也減呢?

您在這里有一個錯誤:

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

這將創建一個臨時的shared_ptr ,該值在函數本地,然后返回對該函數的引用。 那是對不再存在的對象的懸掛引用。 就像getL一樣按值返回(我不知道您為什么稱它為getL ...如果它引用的是左值,那是錯誤的,它將返回一個右值)。

您濫用std::move mo進行了誤導的嘗試以提高性能,但是僅返回對象就更簡單,更安全,並且允許編譯器更有效地對其進行優化。 如果沒有std::move ,將不會有任何復制移動,編譯器將完全消除臨時文件,請參見什么是復制省略和返回值優化?

這些其他步驟也是多余的(盡管在這里實際上並沒有害處):

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

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

在這兩種情況下,您都試圖移動已經是右值的東西,這毫無意義。

另外,您std::enable_shared_from_this好地重新實現了std::enable_shared_from_this 擺脫您的weak_ptr成員和backref,然后執行以下操作:

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

然后調用keeper->shared_from_this()而不是keeper->getL() 您會注意到, shared_from_this()按值而不是按引用返回的,以避免getR()函數中的錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM