通过属性值从向量中删除对象的 unique_ptr
removing a unique_ptr of an object from a vector by an attribute value
我有一个具有这三个功能的 Holder 对象
unique_ptr<Object> Holder::remove(string objName){
std::vector<unique_ptr<Object>>::iterator object =
find_if(objects.begin(), objects.end(),
[&](unique_ptr<Object> & obj){ return obj->name() == objName;}
);
objects.erase(std::remove(objects.begin(), objects.end(), *object));
return std::move(*object);
}
vector<unique_ptr<Object>> const& Holder::getContent() const {
return this->objects;
}
void Holder::add(unique_ptr<Object> objPtr) {
this->objects.push_back(move(objPtr));
}
我写了一个CPP单元测试如下:
void HolderTest::removeObject() {
Holder holder("bag");
unique_ptr<Object> ringPtr(new Object("a"));
holder.add(move(ringPtr));
unique_ptr<Object> swordPtr(new Object("b"));
holder.add(move(swordPtr));
holder.remove("a");
vector<unique_ptr<Object>> const& objects = holder.getContent();
CPPUNIT_ASSERT(objects.size() == 1);
}
这个测试顺利通过,但令我感到奇怪的是,如果我添加以下行:
const std::string name = objects[0].get()->name();
CPPUNIT_ASSERT_EQUALS("b", name);
然后测试崩溃,没有任何消息。我在另一个测试中写了这一行,没有调用 remove,它工作没有任何问题。
如果我将向量大小的值更改为 2 或 0
CPPUNIT_ASSERT(objects.size() == 2);
然后测试失败。所以似乎 remove 函数保留了 unique_ptr 之一,但它把它变成了 nullptr?
请问是什么问题?
std::vector<unique_ptr<Object>>::iterator object =
find_if(objects.begin(), objects.end(),
[&](unique_ptr<Object> & obj){ return obj->name() == objName;}
);
objects.erase(std::remove(objects.begin(), objects.end(), *object));
return std::move(*object);
您取消引用迭代器 object
之后它已失效。参见 Iterator invalidation rules
把指针移到擦除前就可以了
其他说明:
- 将
removing
与值一起使用很有趣(而不是仅仅删除您获得的迭代器)。您是否希望向量包含重复项?实际上,打那个:这会使 erase
错误,因为 它总是删除一个元素 。
- 您也没有在取消引用之前检查
object
可能是 end()
迭代器。 Undefined Behaviour 的另一个来源
- 考虑将
name
提高到 const&
以提高效率
#include <memory>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
struct Object {
Object(std::string name) : _name(std::move(name)) { }
std::string const& name() const { return _name; }
private:
std::string _name;
};
struct Holder {
using Ptr = unique_ptr<Object>;
Ptr remove(string const& objName) {
auto it = find_if(objects.begin(), objects.end(), [&](Ptr& obj){ return obj->name() == objName; });
if (it != objects.end()) {
auto retval = std::move(*it);
objects.erase(it);
return std::move(retval);
}
return {}; // or handle as error?
}
vector<Ptr> const& getContent() const {
return this->objects;
}
void add(Ptr objPtr) {
this->objects.push_back(move(objPtr));
}
private:
vector<Ptr> objects;
};
int main() {
Holder h;
for(auto n: { "aap", "noot", "mies", "broer", "zus", "jet" })
h.add(std::make_unique<Object>(n));
h.remove("broer");
h.remove("zus");
for (auto& o : h.getContent())
std::cout << o->name() << "\n";
}
版画
aap
noot
mies
jet
我有一个具有这三个功能的 Holder 对象
unique_ptr<Object> Holder::remove(string objName){
std::vector<unique_ptr<Object>>::iterator object =
find_if(objects.begin(), objects.end(),
[&](unique_ptr<Object> & obj){ return obj->name() == objName;}
);
objects.erase(std::remove(objects.begin(), objects.end(), *object));
return std::move(*object);
}
vector<unique_ptr<Object>> const& Holder::getContent() const {
return this->objects;
}
void Holder::add(unique_ptr<Object> objPtr) {
this->objects.push_back(move(objPtr));
}
我写了一个CPP单元测试如下:
void HolderTest::removeObject() {
Holder holder("bag");
unique_ptr<Object> ringPtr(new Object("a"));
holder.add(move(ringPtr));
unique_ptr<Object> swordPtr(new Object("b"));
holder.add(move(swordPtr));
holder.remove("a");
vector<unique_ptr<Object>> const& objects = holder.getContent();
CPPUNIT_ASSERT(objects.size() == 1);
}
这个测试顺利通过,但令我感到奇怪的是,如果我添加以下行:
const std::string name = objects[0].get()->name();
CPPUNIT_ASSERT_EQUALS("b", name);
然后测试崩溃,没有任何消息。我在另一个测试中写了这一行,没有调用 remove,它工作没有任何问题。 如果我将向量大小的值更改为 2 或 0 CPPUNIT_ASSERT(objects.size() == 2); 然后测试失败。所以似乎 remove 函数保留了 unique_ptr 之一,但它把它变成了 nullptr? 请问是什么问题?
std::vector<unique_ptr<Object>>::iterator object =
find_if(objects.begin(), objects.end(),
[&](unique_ptr<Object> & obj){ return obj->name() == objName;}
);
objects.erase(std::remove(objects.begin(), objects.end(), *object));
return std::move(*object);
您取消引用迭代器 object
之后它已失效。参见 Iterator invalidation rules
把指针移到擦除前就可以了
其他说明:
- 将
removing
与值一起使用很有趣(而不是仅仅删除您获得的迭代器)。您是否希望向量包含重复项?实际上,打那个:这会使erase
错误,因为 它总是删除一个元素 。 - 您也没有在取消引用之前检查
object
可能是end()
迭代器。 Undefined Behaviour 的另一个来源
- 考虑将
name
提高到const&
以提高效率
#include <memory>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
struct Object {
Object(std::string name) : _name(std::move(name)) { }
std::string const& name() const { return _name; }
private:
std::string _name;
};
struct Holder {
using Ptr = unique_ptr<Object>;
Ptr remove(string const& objName) {
auto it = find_if(objects.begin(), objects.end(), [&](Ptr& obj){ return obj->name() == objName; });
if (it != objects.end()) {
auto retval = std::move(*it);
objects.erase(it);
return std::move(retval);
}
return {}; // or handle as error?
}
vector<Ptr> const& getContent() const {
return this->objects;
}
void add(Ptr objPtr) {
this->objects.push_back(move(objPtr));
}
private:
vector<Ptr> objects;
};
int main() {
Holder h;
for(auto n: { "aap", "noot", "mies", "broer", "zus", "jet" })
h.add(std::make_unique<Object>(n));
h.remove("broer");
h.remove("zus");
for (auto& o : h.getContent())
std::cout << o->name() << "\n";
}
版画
aap
noot
mies
jet