我想填充top: 50%
个std::set
个对象,并检查该集合中是否存在具有相同值的另一个GraphNode
。在Java中,可以通过重载equals和compareTo方法来比较对象,而不是创建一些仿函数对象。我实现了GraphNode
并希望在集合中找到这样的对象,
operator==(T& t)
但我没有在std::find(nodesSet->begin(),nodesSet->end(), new GraphNode<T>(1))!=nodesSet->end())
和==
运算符函数中得到断点。为什么会这样?有没有办法通过对象比较找到对象?
()()
答案 0 :(得分:2)
正如 aschepler 所指出的那样,代码的问题在于你最终会比较指针,而不是对象。 std::find
(查看链接页面中可能的实现),如果在没有谓词的情况下调用,则使用==
运算符来比较当您给它的迭代器被解除引用时返回的内容。在您的情况下,您有一个std::set<GraphNode<T>*> nodesSet
,因此*nodesSet.begin()
的类型为GraphNode<T>*
,而不是GraphNode<T>
(请注意缺少明星)。为了使您能够使用为==
定义的GraphNode
运算符,您需要将您的集合设为std::set<GraphNode<T>>
,这是您的类型的对象而不是指针。
如果必须在集合中存储指针(例如,因为您不想复制对象),则可以为指针编写一个包装器,该指针使用指针的基础类的比较运算符。这是一个例子:
#include <iostream>
#include <set>
#include <algorithm>
class obj {
int i;
public:
obj(int i): i(i) { }
bool operator<(const obj& o) const { return i < o.i; }
bool operator==(const obj& o) const { return i == o.i; }
int get() const { return i; }
};
template <typename T>
class ptr_cmp {
T* p;
public:
ptr_cmp(T* p): p(p) { }
template <typename U>
bool operator<(const ptr_cmp<U>& o) const { return *o.p < *p; }
template <typename U>
bool operator==(const ptr_cmp<U>& o) const { return *o.p == *p; }
T& operator*() const { return *p; }
T* operator->() const { return p; }
};
int main(int argc, char* argv[])
{
obj five(5), seven(7);
std::set<ptr_cmp<obj>> s;
s.insert(&five);
s.insert(&seven);
obj x(7);
std::cout << (*std::find(s.begin(),s.end(), ptr_cmp<obj>(&x)))->get()
<< std::endl;
return 0;
}
事实证明,我的编译器(gcc 6.2.0)要求operator==
和operator<
std::find
在没有谓词的情况下工作。
虽然使用谓词有什么问题?这是一种更通用的方法。这是一个例子:
#include <iostream>
#include <set>
#include <algorithm>
class obj {
int i;
public:
obj(int i): i(i) { }
bool operator==(const obj& o) const { return i == o.i; }
int get() const { return i; }
};
template <typename T>
struct ptr_cmp {
const T *l;
ptr_cmp(const T* p): l(p) { }
template <typename R>
bool operator()(const R* r) { return *l == *r; }
};
template <typename T>
ptr_cmp<T> make_ptr_cmp(const T* p) { return ptr_cmp<T>(p); }
int main(int argc, char* argv[])
{
obj five(5), seven(7);
std::set<obj*> s;
s.insert(&five);
s.insert(&seven);
obj x(7);
std::cout << (*std::find_if(s.begin(),s.end(), make_ptr_cmp(&x)))->get()
<< std::endl;
return 0;
}
注意,make_ptr_cmp
允许您避免明确说明类型,因此您可以编写通用代码。
如果你可以使用C ++ 11,使用可以只使用lambda函数而不是ptr_cmp
,
std::find_if(s.begin(),s.end(), [&x](const obj* p){ return *p == x; } )
答案 1 :(得分:1)
std::find
比较迭代器指向的值。这些值是指针,而不是对象。所以它们都不等于new GraphNode<T>(1)
,这是指向全新物体的全新指针。
答案 2 :(得分:1)
正如其他人所说,你正在比较指针,它们不会按预期工作,它会对内存中的地址进行比较。操作a < b
对于指针具有有效含义,但是将按元素在内存中的位置对元素进行排序,而不是对其包含的数据元素进行排序,并且没有元素将是唯一的,因为它们都将具有唯一的地址。这是除非你尝试两次插入相同的元素。
然而,使用std::find
将隐藏上述问题,无论如何迭代容器中的所有元素。如果您使用的是set
,那么您应该渴望获得元素的对数时间查找,因此应该使用set
自己的find
函数,该函数知道它是一个二叉树下的罩。
在C ++中,Object#equals
的等价物是operator==
(如您所知),在关联容器的上下文中,Object#compareTo
的等价物是operator<
。 Object#equals
和operator==
以同样的方式工作,完全符合您的预期;如果某些事情等于平等,易于理解。算法以不同的方式使用Object#compareTo
和operator<
,operator<
用于实现strict weak ordering以确定一个元素是否小于或大于另一个元素。
因此,为了让您的元素在set
中可用,您需要在operator<
课程中重写GraphNode
。完成后,您可以使用std::set::find
函数查找集合中的元素,它将在O(log n)时间而不是线性时间内找到它们。
这些算法是基于他们正在处理值类型的假设而设计的,即不是指针而是指向那些指针的东西。因此,要使用指针,您需要定义一个新的比较函数,该函数在应用比较之前基本取消引用指针(==
或<
)。
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
template<typename>
class Graph
{
};
template<class T>
class GraphNode
{
friend class Graph<T>;
friend bool operator==(const GraphNode<T>& a, const GraphNode<T>& b);
private:
T t;
std::vector<GraphNode<T>*> adjNodes;
public:
explicit GraphNode(const T& tval)
:t(tval)
{}
T& getT(){ return t; }
const T& getT() const { return t; }
bool operator==(const T& t);
friend bool operator<(const GraphNode& a, const GraphNode& b){
return a.t < b.t;
}
};
template<class T>
inline bool GraphNode<T>::operator==(const T& t)
{
return (this->t == t);
}
template<class T>
inline bool operator==(const GraphNode<T>& a, const GraphNode<T>& b)
{
return (a.t == b.t);
}
int main()
{
using IntGraphNode = GraphNode<int>;
std::set<IntGraphNode> nodesSet;
nodesSet.insert(IntGraphNode(1));
nodesSet.insert(IntGraphNode(2));
auto findit = nodesSet.find(IntGraphNode(1));
if(findit != nodesSet.end())
{
std::cout << "found value\n";
}
auto findit2 = std::find_if(
nodesSet.begin(),
nodesSet.end(),
[](IntGraphNode i) { return i.getT() == 1;});
if(findit2 != nodesSet.end())
{
std::cout << "found value aswell\n";
}
}
第一次搜索使用set
自己的查找函数,第二次搜索使用std::find_if
,它使用谓词(返回true或false的函数)来测试相等性。第二个例子还通过公开T
对象并在比较lambda函数中使用它来消除了制作虚拟对象的需要。
还有关于
的评论std::find(nodesSet->begin(),nodesSet->end(), new GraphNode<T>(1))!=nodesSet->end())
这一方面存在很多概念上的误解。首先std::find
没有采用比较函数,即std::find_if
,但编译器会告诉你(以它自己特别是间接和冗长的方式)。此外,在算法中评估比较函数,您试图在呼叫站点评估它。另一件事与java不同,你不能只是忘掉new
对象。这是内存泄漏,您不再有任何存储new
ed值的变量,因此您无法delete
它。