簡體   English   中英

一組weak_ptr

[英]A set of weak_ptr

這是代碼:

struct lex_compare {
    bool operator() (const weak_ptr<int> &lhs, const weak_ptr<int> &rhs)const {
        return *lhs.lock() < *rhs.lock();
    }
};

int main(){
    set<weak_ptr<int>,lex_compare> intset;
    intset.insert(make_shared<int>(1));

    cout << "intset size:" << intset.size() << endl; //1
    cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // failed

}

我想知道如何count/find存儲在intsetweak_ptr<int>以及是否有更好的方法可以完成同樣的工作?

您不能將臨時 shared_ptr 插入一組弱指針,因為從這個存儲的弱指針指向已刪除的內存的意義上說,這是內存泄漏。

intset.insert(make_shared<int>(1)); 
// after this instruction shared_ptr destructor frees the memory

這就是為什么你不能在 set 中找到它 - 因為*lhs.lock()在這里是 UB。

請參閱weak_ptr::lock 文檔

您需要以這種方式制作 òrder 運算符:

struct lex_compare {
    bool operator() (const weak_ptr<int> &lhs, const weak_ptr<int> &rhs)const {
        auto lptr = lhs.lock(), rptr = rhs.lock();
        if (!rptr) return false; // nothing after expired pointer 
        if (!lptr) return true;  // every not expired after expired pointer
        return *lptr < *rptr;
    }
};

所有這一切意味着 - 你需要有這個 shared_ptr sowmewhere 來計算它:

int main(){
    set<weak_ptr<int>,lex_compare> intset;
    auto shared1 = make_shared<int>(1); 
    intset.insert(shared1);

    cout << "intset size:" << intset.size() << endl; //1
    cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // failed
}

有了上述 - 您的計數將起作用。

還考慮保持 shared_ptr 設置...

[更新]

marko 在評論中指出了有效的問題。 std::weak_ptr 根本不能以您使用它的方式用作鍵。 只有當你能確保指向的值永遠不會改變,指針本身也永遠不會過期。 看這個例子:

    set<weak_ptr<int>,lex_compare> intset;
    auto shared1 = make_shared<int>(1); 
    intset.insert(shared1);
    cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // works
    shared1.reset();
    cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // failed

另一個例子:

    set<weak_ptr<int>,lex_compare> intset;
    auto shared1 = make_shared<int>(1); 
    intset.insert(shared1);
    cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // works
    *shared1 = 2;
    cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // failed

您可以保留 std::shared_ptr 以防止指針的設置過期 - 並且 std::shared_ptr 具有operator < - 但該運算符比較指針本身 - 而不是指向的值 - 所以std::set<std::shared_ptr<int>>更好std::set<std::shared_ptr<int>> - 但最好的是std::set<int>

或更改std::set<...> --> std::vector<std::weak_ptr<int>> - 並使用count_if -- 請參閱:

vector<weak_ptr<int>> intset;
auto shared1 = make_shared<int>(1);
intset.push_back(shared1);
cout << "Does 1 exist?"<< count_if(begin(intset), end(intset), 
                                  [](auto&& elem) 
                                  { 
                                     auto ptr = elem.lock();
                                     return ptr && *ptr == 1; 
                                  }); 

或者使用std::set<std::shared_ptr<int>>

set<shared_ptr<int>> intset;
auto shared1 = make_shared<int>(1);
intset.insert(shared1);
// if you can ensure shared1 value will not change:
cout << "Does 1 exist?"<< intset.count(shared1);
// if not  - use count_if - the slower than std::count
cout << "Does 1 exist?"<< count_if(begin(intset), end(intset), 
                                  [](auto&& ptr) 
                                  { 
                                     return ptr && *ptr == 1; 
                                  }); 

shared_ptr沒有實現您似乎假設的享元模式。

make_shared返回一個可以共享的指針。 要獲得更多指向同一對象的指針,您必須使用復制構造函數或復制賦值運算符,並傳遞現有的shared_ptr

make_shared不會生成指向現有對象的附加指針。 它創建一個新對象。

因此, intset.count(make_shared<int>(1))返回0是正確的。 剛剛創建的shared_ptr<int>在集合中的任何地方都不存在。

連續調用make_shared<int>(1)比較不相等。

然后是與您的比較功能相關的額外損壞。 有一個適用於weak_ptr的排序,但事實並非如此。 你不穩定的比較函數會導致set行為異常。

您應該簡單地使用set<int>

我們可以使用標准的std::owner_less<>代替嘗試為弱指針編寫自己的比較函數並提出行為不端的解決方案。

除非您實現清理方法並定期使用它,否則擁有一組弱指針是一個壞主意。 您可能希望在比較函數中設置保護措施,因為目前結果未定義。 以@PiotrNycz 的建議為例:

template <class T>
struct wptr_less_than 
{
    bool operator() ( const std::weak_ptr<T>& lhs, const std::weak_ptr<T>& rhs ) const {
        return lhs.expired() || (!rhs.expired() && *lhs.lock() < *rhs.lock());
    }
};

計算有效的弱指針

使用count_ifweak_ptr::expired的組合:

template <class T, class C, class A>
size_t count_valid_pointers( const std::set< std::weak_ptr<T>, C, A >& s )
{
    return s.size() - std::count_if( s.begin(), s.end(), 
         []( const std::weak_ptr<T>& wptr ){ return wptr.expired(); } 
    );
}

查找具有特定值的元素

您可以使用靜態共享指針來存儲查詢(盡管這有點難看):

template <class T, class C, class A>
typename std::set< std::weak_ptr<T>, C, A >::iterator
find_value( const std::set< std::weak_ptr<T>, C, A >& s, const T& val )
{
    static auto query = std::make_shared<T>();
    query.reset( const_cast<T*>(&val), []( T* ){} ) ;
    return s.find(query);
}

和一個例子:

#include <algorithm>
#include <iostream>
#include <memory>
#include <set>

template <class T>
struct wptr_less_than 
{
    bool operator() ( const std::weak_ptr<T>& lhs, const std::weak_ptr<T>& rhs ) const {
        return lhs.expired() || (!rhs.expired() && *lhs.lock() < *rhs.lock());
    }
};

template <class T, class C, class A>
size_t count_valid_pointers( const std::set< std::weak_ptr<T>, C, A >& s )
{
    return s.size() - std::count_if( s.begin(), s.end(), 
         []( const std::weak_ptr<T>& wptr ){ return wptr.expired(); } 
    );
}

template <class T, class C, class A>
typename std::set< std::weak_ptr<T>, C, A >::iterator
find_value( const std::set< std::weak_ptr<T>, C, A >& s, const T& val )
{
    static auto query = std::make_shared<T>();
    query.reset( const_cast<T*>(&val), []( T* ){} ) ;
    return s.find(query);
}


int main()
{
    std::set< std::weak_ptr<int>, wptr_less_than<int> > intset;

    auto a = std::make_shared<int>(1);
    auto b = std::make_shared<int>(2);

    intset.insert(a); intset.insert(b); a.reset();

    std::cout << "intset size:" << intset.size() << std::endl; //2
    std::cout << "intset real size:" << count_valid_pointers(intset) << std::endl; //1

    if ( find_value(intset,2) != intset.end() )
        std::cout << "Found it!\n";
}

將 weak_pointer 包裝在容器中

例如

template <class T> class WeakPtrAsKey {
  T * m_ptr;
    typedef std::shared_ptr<T> _Sptr;
    typedef std::weak_ptr<T> _Wptr;
    _Wptr m_wptr;
    public:
    WeakPtrAsKey():m_ptr(nullptr) {};
    WeakPtrAsKey(_Wptr wptr):m_ptr(wptr.lock().get()),m_wptr(wptr) {}
    WeakPtrAsKey(_Sptr sptr):m_ptr(sptr.get()),m_wptr(sptr) {}
    bool operator<(const WeakPtrAsKey<T> &other) const { return m_ptr<other.m_ptr;}
    bool operator==(const WeakPtrAsKey<T> &other) const { return m_ptr==other.m_ptr;}
    _Wptr getWeak() const { return m_wptr;}
    _Sptr lock() const { return m_wptr.lock();}
};

並將其用作:

std::set<WeakPtrAsKey<MyKey>> mySet;
std::map<WeakPtrAsKey<MyKey>,MyVal> myMap;

用法示例:

void addToMap(std::weak_ptr<MyKey> key, const MyVal &val)
{
  myMap[key]=val
}
void addToMap(std::shared_ptr<MyKey> key, const MyVal &val)
{
  myMap[key]=val
}
std::shared_ptr<MyKey> getFirstKey()
{
  auto it=myMap.begin();
  return=it->first.lock();
}

暫無
暫無

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

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