我有两个这样的函数。当我添加一个新对象时,一切正常,但如果我试图添加一个已经在这个向量中的对象,应用程序就会关闭。我正在运行unique_ptr
析构函数后,从一个矢量删除。如何解决这个问题?
void AddObject(GameObject* obj) {
objects().push_back(std::move(unique_ptr<GameObject>(obj)));
}
void DeleteObject(GameObject* obj) {
for (auto itr = objects().begin(); itr != objects().end(); ++itr) {
if ((*itr)->ID == obj->ID) {
objects().erase(std::move(itr));
itr->~unique_ptr();
}
}
}
你所展示的代码都是错误的。
AddObject()
应采用unique_ptr<GameObject>
作为输入,而不是原始的GameObject*
指针,例如:
void AddObject(unique_ptr<GameObject> obj) {
objects().push_back(std::move(obj));
}
否则,调用者可能会多次添加同一个对象,因此它将由多个unique_ptr
管理。
一个更好的选择是不让调用者开始创建自己的对象。让调用者指定对象类型和构造函数参数,但让AddObject()
处理实际创建,例如:
template<typename T, typename... Args>
void AddObject(Args&&... args) {
objects().push_back(std::make_unique<T>(std::forward<Args>(args)...));
}
对于DeleteObject()
,erase()
从vector中获取元素会使指向擦除点()后的元素的迭代器失效,包括被擦除的迭代器。你的循环没有考虑到这一点。erase()
返回一个迭代器到被擦除的元素之后的元素,所以如果你不打算在循环中使用break
(你可以有多个具有相同ID
值的对象吗?),那么你需要使用iterator
来正确地继续循环。
同样,直接调用不是用placement-new
创建的对象的析构函数是未定义行为。在这种情况下,根本没有理由手动销毁unique_ptr
。当它们从vector中移出作用域时,它们将自动被销毁。
如果你想让DeleteObject()
搜索一个特定的对象,那么它应该寻找那个特定的对象指针,而不是一个ID:
void DeleteObject(GameObject* obj) {
auto &objs = objects();
for (auto itr = objs.begin(); itr != objs.end(); ++itr) {
if (itr->get() == obj) {
objs.erase(itr);
return;
}
}
}
另外:
void DeleteObject(GameObject* obj) {
auto &objs = objects();
auto itr = std::find_if(objs.begin(), objs.end(),
[=](auto &ptr){ return ptr.get() == obj; }
);
if (itr != objs.end()) {
objs.erase(itr);
}
}
如果你想让DeleteObject()
搜索一个ID,那么它应该接受一个ID作为输入,而不是一个对象指针:
void DeleteObject(int ID) {
auto &objs = objects();
for (auto itr = objs.begin(); itr != objs.end(); ++itr) {
if ((*itr)->ID == ID) {
itr = objs.erase(itr);
return;
}
}
}
另外:
void DeleteObject(int ID) {
auto &objs = objects();
auto itr = std::find_if(objs.begin(), objs.end(),
[=](auto &ptr){ return ptr->ID == ID; }
);
if (itr != objs.end()) {
objs.erase(itr);
}
}
,如果多个对象可以有相同的ID (?):
void DeleteObject(int ID) {
auto &objs = objects();
for (auto itr = objs.begin(); itr != objs.end(); ) {
if ((*itr)->ID == ID) {
itr = objs.erase(itr);
}
else {
++itr;
}
}
}
另外:
void DeleteObject(int ID) {
auto &objs = objects();
objs.erase(
std::remove_if(objs.begin(), objs.end(),
[=](auto &ptr){ return ptr->ID == ID; }
),
objs.end()
);
// or, in C++20 and later:
std::erase_if(objs,
[=](auto &ptr){ return ptr->ID == ID; }
);
}