编辑:看错了一行,这是一套当然的,谢谢你发现我的(懒惰)错误。
以下示例代码:
set<Foo *> set_of_foos;
set_of_foos.push_back(new Foo(new Bar("x")));
set_of_foos.push_back(new Foo(new Bar("y")));
[...]
//the way a "foo" is found is not important for the example
bool find_foo(Foo *foo) {
return vector_of_foos.end() != vector_of_foos.find(s)
}
现在我做的时候
find_foo(new Foo(new Bar("x")));
它返回当然是假的,因为它无法找到。原因很明显(指针指向不同的对象,因为它们分配了new
,导致地址的不同值)给我。
我想比较Foo
(上例中的“x”)而不是Foo*
本身的竞争。使用boost不是一个选项,以及修改Foo。
我是否需要循环遍历Foo *
内的set_of_foos
或者是否有更简单的解决方案?我尝试对每个Foo
的内容进行唯一序列化,并将set<Foo *>
替换为map<string, Foo *>
,但这似乎是一个非常“被黑客攻击”的解决方案而且效率不高。
答案 0 :(得分:5)
使用您的自定义可比较功能将vector
更改为set
,以比较Foo
个对象。
应该是:
struct ltFoo
{
bool operator()(Foo* f, Foo* s) const
{
return f->value() < s->value();
}
};
set<Foo*, ltFoo> sFoo;
sFoo.insert(new Foo(new Bar("x"));
sFoo.insert(new Foo(new Bar("y"));
if (sFoo.find(new Foo(new Bar("y")) != sFoo.end())
{
//exists
}
else
{
//not exists
}
答案 1 :(得分:2)
find_foo(new Foo(new Bar("x")));
听起来不是一个好主意 - 它很可能(在任何情况下)都会导致内存泄漏。
您可以将find_if与仿函数一起使用:
struct comparator {
Foo* local;
comparator(Foo* local_): local(local_) {}
~comparator() { /* do delete if needed */ }
bool operator()(const Foo* other) { /* compare local with other */ }
};
bool found = vec.end() != std::find_if(vec.begin(), vec.end(), comparator(new Foo(...)));
答案 2 :(得分:1)
不使用std :: find,而是使用std :: find_if并提供自己的谓词。这当然依赖于您能够访问在Foo中保存“x”的成员。
struct FooBar
{
FooBar(Foo* search) : _search(search){}
bool operator(const Foo* ptr)
{
return ptr->{access to member} == _search->{access to member};
}
Foo* _search;
}
vector<Foo*>::iterator it = std::find_if(vec.begin(), vec.end(), FooBar(new Foo(new Bar("x")));
如果您无法访问该成员并且您可以保证所有其他成员都是相同的,您可以在上面的仿函数中尝试使用简单的memcmp,而不是“==”。
答案 3 :(得分:1)
我是否需要遍历vector_of_foos中的每个Foo *或者是否有更简单的解决方案?
你需要循环找到你想要的东西,但你可以使用std :: find_if或其他“包裹循环”。这在C ++ 0x中使用lambdas更自然,但在C ++ 03中我只使用常规for循环,如果你需要在多个地方执行此操作,可能包含在你自己的函数中。
答案 4 :(得分:0)
您也可以考虑使用Boost Ptr container library。它允许使用标准算法,查找等指示列表,就好像它包含对象一样,并在向量删除时自动释放指针使用的内存。
答案 5 :(得分:0)
我有同样的问题,最后写了一个简单的DereferenceCompare类来完成这项工作。我很想知道其他人对此的看法。问题的关键在于现有的答案要求程序员使用你的集合以一种容易泄漏内存的不寻常方式访问它,即通过将临时地址传递给std::set::find()
或通过{{1 }}。如果您要以非标准方式访问标准容器,那么使用标准容器的重点是什么? Boost有一个很好的容器库来解决这个问题。但是,由于在C ++ 14中引入了透明比较器,您可以编写一个自定义比较器,使std::find_if()
和std::set::insert()
按预期工作,而不依赖于Boost。您可以将其用作std::set:find()
std::set< Foo*, DereferenceCompare<Foo, YourFooComparator> > set_of_foos;
答案 6 :(得分:0)
如果可以利用C++11功能,那么也可以使用lambda expression而不是定义比较对象,
如其他答案所示。为了使下面的示例代码正常工作,我从您的代码中定义了Bar
和Foo
,如下所示:
struct Bar {
Bar(std::string s) : str(s) {}
std::string str;
};
struct Foo {
Foo(Bar* p) : pBar(p) {}
Bar* pBar;
};
如果您提供以下lambda表达式作为std::set
的键比较函数,
然后比较您的内容(即字符串"x"
和"y"
),而不是指向内容的指针。
因此,find()
也会按预期工作,如以下代码所示:
int main() {
auto comp = [](const Foo* f1, const Foo* f2) { return f1->pBar->str < f2->pBar->str; };
std::set<Foo*, decltype(comp)> set_of_foos(comp);
set_of_foos.emplace(new Foo(new Bar("x")));
set_of_foos.emplace(new Foo(new Bar("y")));
auto it = set_of_foos.find(new Foo(new Bar("x")));
if (it == std::end(set_of_foos))
std::cout << "Element not found!" << std::endl;
else
std::cout << "Element found: " << (*it)->pBar->str << std::endl;
return 0;
}
输出:
找到的元素:x
注意: std::set
仅允许唯一的条目(即键)。根据提供的键比较功能确定条目是否唯一。
对于上面的代码,这意味着即使pBar->str == "x"
或Bar
存储在不同的地址,您也只能用Foo
存储单个条目。
例如,如果要使用pBar->str == "x"
存储多个条目,则必须使用std::multiset
。