比较第一个向量的不同矢量迭代器和擦除元素
Compare different vector iterators and erase element from the first vector
我一直在为这个问题而苦苦挣扎大约一周。我不知道问题是什么,或者我在错误的地方更新迭代器。
让我们指出。我正在尝试制作一个下降系统,在玩家杀死玩家之后,从怪物中删除了随机物品。我从下面的容器中获取物品。从gear
容器中收到项目后,我也想从gear
向量删除收到的项目。IE。如果掉落"板装甲",我想从gear
容器中删除它。
我有一个齿轮矢量,我在其中注册了不同的齿轮,例如武器,装甲或配件。在我们的情况下,我只专注于 armor 。
std::vector<std::unique_ptr<Armor>> gear;
/* This is the simplified version of the vector.
I register different elements into my gear vector. Now is only armor focused on.
*/
gear.emplace_back(new Armor("Great pauldron", 25, 100));
gear.emplace_back(new Armor("Holy armor", 3, 18));
gear.emplace_back(new Armor("Dominic's eye", 18, 73));
gear.emplace_back(new Armor("Plate armor", 23, 21));
gear.emplace_back(new Armor("Poor armor", 57, 7));
gear.emplace_back(new Armor("Good shield", 91, 5));
gear.emplace_back(new Armor("Jodin's boots", 18, 66));
gear.emplace_back(new Armor("Ivona's gauntlets", 25, 100));
下一步,我上了一个班级,在其中我将其作为向量迭代器的向量的项目数量。(我将公共功能用于此类操作。)
class Maker {
private:
template<typename Iter, typename RandomGen>
Iter randomSelection(Iter begin, Iter end, RandomGen& ran) {
std::uniform_int_distribution<> dist(0, std::distance(begin, end) - 1);
std::advance(begin, dist(ran));
return begin;
}
public:
template<typename Iter>
std::vector<Iter> randomSelection(Iter& begin, Iter& end, int amount) {
std::vector<Iter> it;
std::random_device randDev;
std::mt19937 gen(randDev());
for (int i = 0; i < amount; i++)
it.push_back(randomSelection<>(begin, end, gen));
return it;
}
};
接下来,我制作一个向量迭代器的向量,以从gear
容器中接收随机项目。
Maker mak;
std::vector<std::vector<std::unique_ptr<Armor>>::iterator>& droppedItems =
mak.randomSelection(gear.begin(), gear.end(), 5);
问题到了,我试图比较两个向量和如果发现的盔甲名称;将其从我们的第一个gear
矢量中删除。我几乎总是遇到访问违规错误。有时它可以删除项目而不会产生任何错误。但是每次一次即可尝试一次。
for (auto& i = gear.begin(); i != gear.end();) {
for (auto& j = droppedItems.begin(); j != droppedItems.end(); j++) {
/* This if statement is where I get the access violation error; 0x05.*/
if (i->get()->getName() == (*j)->get()->getName()) {
std::cout << std::endl << i->get()->getName() << " has been deleted!n";
i = gear.erase(i);
}
else
i++;
}
}
我假设我会在错误的位置增加迭代器。我认为我的擦除操作很好,但我实际上是没有想法的。
std::vector<T>::erase(iter)
在 iter
(包括 end()
)时或之后,迭代器和引用无效,但是您仍然尝试使用这些无效的迭代器,此后 droppedItems
containe。
例如,如果说droppedItems
还包含指向gears
的最后一个元素以及其他一些迭代器的迭代器。当您删除gears
的任何其他元素时,它将迭代器无效到最后一个元素。因此,当您最终调用gears.erase()
将(无效的)迭代器传递到最后一个元素时,它将导致不确定的行为。
std::vector<int> test{1,2,3,4};
auto firstIter = test.begin();
auto secondIter = firstIter + 1;
auto thirdIter = secondIter + 1;
auto fourthIter = thirdIter + 1;
test.erase(secondIter); // Invalidates secondIter, thirdIter and fourthIter
// but not firstIter
您可以将std::vector<T>
迭代器视为T
的指针。如果删除一个元素,则将其后面的元素将其移动到内存中,以将向量元素作为动态数组保持。因此,迭代器"指向"这些元素不会继续指向正确的元素。
test.erase(secondIter)
之前:
address: | 0x0 | 0x1 | 0x2 | 0x3 |
value: | 1 | 2 | 3 | 4 |
firstIter = 0x0 // Points to element with value 1
secondIter = 0x1 // Points to element with value 2
thirdIter = 0x2 // Points to element with value 3
fourthIter = 0x3 // Points to element with value 4
test.erase(secondIter)
之后:
address: | 0x0 | 0x1 | 0x2 |
value: | 1 | 3 | 4 |
firstIter = 0x0 // Still points to element with value 1
secondIter = 0x1 // no longer points to element with value 2
thirdIter = 0x2 // no longer points to element with value 3
fourthIter = 0x3 // out of vector bounds
我建议将gears
的随机元素设置为nullptr
,然后紧凑矢量:
template <typename T>
void freeRandomItems(std::vector<std::unique_ptr<T> > & vec,
std::size_t amount)
{
if (amount == 0u)
return; // Nothing to do
// Setup RNG:
std::random_device randDev;
std::mt19937 gen(randDev());
if (amount == 1u) { // Remove only one random element:
vec.erase(randomSelection(vec.begin(), vec.end(), gen));
return;
}
// Deallocate amount pointed elements in vector, reset pointers:
do {
randomSelection<>(vec.begin(), vec.end(), gen)->reset();
} while (--amount);
// Remove all nullptr elements from vec:
vec.erase(
std::remove_if(
vec.begin(),
vec.end(),
[](std::unique_ptr<T> const & v) noexcept { return !v; }),
vec.end());
}
鉴于您的情况,以不同的方式做事可以避免这种重新验证业务。您可以添加在嵌套环中的齿轮中不匹配的临时矢量中的元素,然后将矢量移动到原始的齿轮矢量中。这样的东西?
更新:在发布的代码中存在一个小错误。修复了相同的。该代码现在正在我的机器上工作并给出正确的结果。
decltype(gear) newGearTmp;
//Reserve size for remaining elements in newGearTmp vector
newGearTmp.reserve(gear.size() - droppedItems.size());
for (auto& i = gear.begin(); i != gear.end(); i++) {
bool lbFound = false;
for (auto& j = droppedItems.begin(); j != droppedItems.end() && !lbFound; j++) {
if (i->get()->getName() == (*j)->get()->getName()) {
std::cout << std::endl << i->get()->getName() << " has been deleted!n";
//i = gear.erase(i); //No need for this
lbFound = true;
}
}
!lbFound ? newGearTmp.push_back(std::move(*i)) : 0;
}
gear = std::move(newGearTmp); //gear now has new armor, unwanted elements are deleted
与您当前的实施相比,这不应该是更大的性能,这也涉及在擦除之后交换元素的开销。
另外,在代码的这一部分中,您应该按值而不是参考获得删除项目的向量。这样。
Maker mak;
std::vector<std::vector<std::unique_ptr<Armor>>::iterator> droppedItems =
mak.randomSelection(gear.begin(), gear.end(), 5);
通过引用进行 droppedItems
指向删除的临时向量,该向量为函数 mak.randomSelection()
的范围。
另外一件事。在您最初的提交代码中,i++
似乎有些不合适。由于i
的增加而无需与所有j
s。
- C++:std::ofstream 方法 open() 在第二次迭代时擦除打开的 ifstream 文件
- 如何使用 std::list 模板的迭代器擦除
- 迭代时擦除设置元素///
- 如何从TBB concurrent_hash_map C++迭代和擦除
- C++ 向量::使用类对象迭代器擦除不擦除向量成员
- 迭代器擦除矢量中的元素无效,但它不会崩溃
- C 列表迭代和擦除
- 无法使用迭代器擦除矢量元素,矢量元素实现移动构造函数
- 映射::擦除:按键擦除或迭代器擦除之间的区别
- 在C++STL映射上通过迭代器擦除
- 使用迭代器擦除矢量中的元素
- STL 允许使用指向不同映射的迭代器擦除映射的键/值?
- std::forward_list -- 使用存储的迭代器擦除
- 映射迭代器擦除未调用适当的析构函数
- 迭代器擦除 (迭代器在先,迭代器最后) 在 Visual C++ 2010 Express 下不起作用
- Boost multi_index反向迭代器擦除麻烦
- 使用迭代器擦除'find'或'remove'
- 列表迭代器擦除"Debug assertion failed"
- std:使用迭代器擦除元素时:d内存损坏
- 向量迭代器擦除给我一个运行时错误