删除元素并将其添加到主循环内的矢量
Delete and add elements to vector inside main loop
我之前搜索过,但找不到任何答案。我对 c++ 有点陌生,所以希望这个问题不会太愚蠢。
我正在尝试在矢量中添加和删除元素,在我的情况下,在所有粒子上进行大更新或绘制循环期间填充了粒子。例如,删除一些粒子,因为它们死亡,但也添加一些其他粒子,因为一个粒子与物体碰撞,我想显示碰撞点的小粒子爆发。我在演示文件中制作了这个简单的测试代码,以找出问题的根源。
我认为问题是因为我删除并添加粒子,迭代器指针变得无效。删除有效,但是当我添加一些随机删除时,我得到一个空指针。下面的代码有点冗长,我知道我应该将迭代器与 begin() 和 end() 一起使用,但我对这些有同样的问题,并稍微玩了一下代码,尝试更多的 JavaScript 数组样式循环,因为我更熟悉。
void testApp::drawParticles()
{
int i=0;
int max = particles.size();
vector<Particle*>::iterator it = particles.begin();
while ( i < max ) {
Particle * p = particles[i];
if ( ofRandom(1) > .9 ) {
it = particles.erase(it);
max = particles.size();
} else {
ofSetColor(255, 255, 0);
ofCircle( p->x, p->y, p->z, 10);
if ( ofRandom(1) < .1 ) addSomeNewOnes();
i++;
it++;
}
}
}
void testApp::addSomeNewOnes()
{
int max = ofRandom(4);
for ( int i=0; i<max; i++ ) {
Particle * p = new Particle();
p->x = ofRandom( -ofGetWidth()/2, ofGetWidth()/2 );
p->y = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
p->z = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
particles.push_back( p );
}
}
每次插入vector
时,它的迭代器都可能失效。 您不能这样做:
if ( ofRandom(1) < .1 ) addSomeNewOnes();
it++
因为在调用addSomeNewOnes()
后,it
无效。
您可以使用调用 vector::insert
返回的迭代器,但在您的情况下,这意味着重新设计您的代码。
无论如何,这是您可能想要做的事情,因为您的代码有点笨拙。
你可以从最后循环,这应该允许你删除你的当前(因为你只是从末尾删除)并添加新的添加到最后:
Vector<Particle*>::iterator it = particles.end();
while (iter != particles.begin()) {
Particle * p = *iter;
if ( ofRandom(1) > .9 ) {
particles.erase(iter);
} else {
ofSetColor(255, 255, 0);
ofCircle( p->x, p->y, p->z, 10);
if ( ofRandom(1) < .1 ) addSomeNewOnes();
}
iter--;
}
如果不添加,根据此处的信息,STL 中的迭代器是稳定的,因此您应该能够向前迭代并且仍然能够获得相同的结果。
在某些情况下,当基础数据更改时,迭代器将失效。
你必须打破循环,重新开始。
您应该能够通过将整个 drawParticle 函数包装在 while(notDone)
循环中并在完成修改向量时将 notDone 设置为 true 来做到这一点。
这是另一个关于规则的SO问题:迭代器失效规则
it = particles.erase(it);
将返回一个迭代器,指向被擦除元素之后元素的新位置。如果擦除的那个恰好是最后一个,它将指向 particles.end()
. "结束"上的it++
是一个错误。
此外,如果:
if ( ofRandom(1) < .1 ) addSomeNewOnes();
评估为 true,并且调用addSomeNewOnes()
,正如其他人所说,这也将使迭代器无效。
迭代器的位置插入和删除? 㞖插入和擦除有效的返回迭代器,您可以使用那些。 像这样:
std::vector<T>::iterator i = v.begin();
while ( i != v.end() ) {
if ( someCondition ) {
i = v.erase( i );
} else {
++ i;
}
}
是一个或多或少的标准成语。
对于随机插入和删除,您必须使用索引,这您根据插入或删除的元素数量进行更新,并且插入或删除是在当前电流的前面还是后面位置。
有一种简单的方法可以使它正常工作,而不必担心失效规则:只需为下一次迭代构建一个新向量。
typedef vector<Particle*> ParticleVector;
// modifies the particle vector passed in
void testApp::drawParticles(ParticleVector &particles)
{
ParticleVector next;
next.reserve(particles.size()); // seems like a reasonable guess
for (auto it = particles.begin(); it != particles.end(); ++it)
{
Particle *p = *it;
if (ofRandom(1) > .9) {
// don't copy to the next cycle
delete p;
} else {
// copy to the next cycle
next.push_back(p);
ofSetColor(255, 255, 0);
ofCircle(p->x, p->y, p->z, 10);
if (ofRandom(1) < .1)
addSomeNewOnesTo(next);
}
}
particles.swap(next);
}
请注意,当你不使用全局变量时,像这样重构是多么容易,顺便说一句。
void testApp::addSomeNewOnesTo(ParticleVector &particles)
{
int max = ofRandom(4);
for ( int i=0; i<max; i++ ) {
Particle * p = new Particle();
p->x = ofRandom( -ofGetWidth()/2, ofGetWidth()/2 );
p->y = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
p->z = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
particles.push_back( p );
}
}
另一方面:您的实现中没有内存泄漏吗?您正在使用
vector<Particle*> particles
并且还使用particles.erase()
.这不会只是删除指向粒子的指针,而不是创建的对象吗?
- 擦除while循环中迭代的元素
- C++:如何循环通过向量中的整数元素
- 对于set上的循环-获取next元素迭代器
- 基于范围的 for 循环:迭代使用一个元素扩展的向量
- 如何从使用 for 循环中的矢量大小的矢量中删除元素
- C++基于范围的 for 循环和元素副本
- 这个返回元素位置的基于循环的函数有什么问题?
- 为什么我在尝试在单向链表中打印元素时会出现这个永无止境的循环
- 循环访问还包含未使用元素的字符串数组
- C++用于循环跳过向量的元素?
- 基于相邻元素 c++ 的分段误差范围的循环
- 循环中的变量被设置为下一个数组的元素始终具有相同的内存地址?
- 使用现代C++在 Eigen3 矩阵上使用 for 循环进行元素操作的优雅方式
- 需要帮助编写一个小程序来将循环乘以 n 个元素和 k 个多重性
- C++ 数据结构队列:使用 for 循环查找队列中最大的元素
- 将函数应用于特征矩阵中的所有元素,而无需循环
- 使用 C++ 从每个循环的数组中选择 n 个元素
- 忽略元素的基于范围的 for 循环
- 优先级队列(使用 pairs<int,int> ) 根据 for 循环迭代器的顶级元素
- 遍历 STL 映射(集/多集)的最佳方法,同时元素可能会在循环期间被删除并重新插入?