以下是代码:
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
weak_ptr<int>
存储在intset
中,是否有更好的方法可以执行相同的工作?
答案 0 :(得分:4)
你不能将临时的shared_ptr插入到弱指针集中,因为这是存储器泄漏,因为这个存储的弱指针指向已经删除的内存。
intset.insert(make_shared<int>(1));
// after this instruction shared_ptr destructor frees the memory
这就是为什么你无法在集合中找到它 - 因为*lhs.lock()
在这里是UB。
你需要以这种方式建立你的运营商:
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保留在set ...
中<强> [UPDATE] 强>
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 <
- 但是这个运算符比较指针本身 - 而不是指向的值 - 所以更好{{ {1}} - 但最好的是std::set<std::shared_ptr<int>>
或更改std::set<int>
- &gt; 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>>
答案 1 :(得分:2)
shared_ptr
没有实现你想象的flyweight模式。
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>
。
答案 2 :(得分:1)
与其尝试为弱指针编写自己的比较函数并提出行为不当的解决方案,不如使用标准的 std::owner_less<>
。
答案 3 :(得分:0)
除非你实现一个清理方法并定期使用它,否则拥有一组弱指针是一个坏主意。您可能希望在比较函数中添加一个安全措施,因为目前结果是未定义的。以@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());
}
};
使用http://bez-granic.eu/lc/和count_if
的组合:
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";
}
答案 4 :(得分:0)
将weak_pointer包装在容器中
e.g。
tab
并将其用作:
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;