在迭代数据结构时将元素添加到数据结构(例如向量)时会发生什么.我可以不这样做吗?
我尝试了这个,它打破了:
int main() {
vector<int> x = { 1, 2, 3 };
int j = 0;
for (auto it = x.begin(); it != x.end(); ++it) {
x.push_back(j);
j++;
cout << j << " .. ";
}
}
Run Code Online (Sandbox Code Playgroud) 关于deque中的迭代器失效,我有点困惑.(在这个问题的背景下)
以下是摘自 - The C++标准库:教程和参考,作者:Nicolai M. Josuttis
除开头或结尾之外的任何元素的插入或删除都会 使引用双端队列元素的所有指针,引用和迭代器无效.
以下是SGI网站的摘录:
deque的迭代器失效的语义如下.Insert(包括
push_front和push_back)使引用deque的所有迭代器无效.在双端队列中间擦除使所有引用双端队列的迭代器无效.只有当它指向已擦除的元素时,在双端队列的开头或结尾处擦除(包括pop_front和pop_back)才会使迭代器无效.
恕我直言,deque是块的集合,第一个块在一个方向上生长,最后一个块在相反方向上生长.
- - -
- - -
| - - ^
| - - |
V - - |
- - -
- - -
Run Code Online (Sandbox Code Playgroud)
push_back, push_front 不应该对deque迭代器产生任何影响(我同意Josuttis).
什么是正确的解释?标准对此有何看法?
我一直认为对象在相同的内存位置开始和结束其生命周期,但是最近我遇到了需要确定的情况。具体来说,我正在从标准中寻找一种保证,即无论编译器执行何种优化,构造对象的地址都将与其调用的析构函数相同,并且其析构函数确实是相同的,除非程序终止,否则请确保从该位置调用它。
我一直认为这些东西是理所当然的,但是仔细检查后我找不到保证,而且在复制和移动省略周围有些语言我不确定如何解释。我希望这里的一些更懂标准的人可以将我引向章节和经文。
假设有一个包含int向量的A类.现在假设创建了A的向量.如果由于push_back而发生A对象的重新分配(因此移动了矢量对象),那么指向int的指针是否仍然有效?这有保证吗?
澄清:
class A {
public:
A() {};
std::vector<int> a = {1,2,3,4,5,6,7,8,9};
};
int main()
{
std::vector<A> aVec(2);
int *x1 = &(aVec[1].a[2]);
A *x2 = &aVec[1];
std::cout << x1 << " - " << x2 << " - " << aVec.capacity() << "\n";
aVec.resize(30);
int *y1 = &(aVec[1].a[2]);
A *y2 = &aVec[1];
std::cout << y1 << " - " << y2 << " - " << aVec.capacity() << "\n";
}
Run Code Online (Sandbox Code Playgroud)
运行此代码可以得到:
0x1810088 - 0x1810028 - 2
0x1810088 - 0x18100c8 - …Run Code Online (Sandbox Code Playgroud) 在我的代码中,a有一个Node对象的全局向量和一个Node指针的局部向量:
#include<cstdio>
#include<cstdlib>
#include<vector>
using namespace std;
class Node {
int n;
public:
Node(int i) : n(i);
int getN() { return n; }
};
vector<Node> v;
int main() {
vector<Node*> p;
v.push_back(Node(1));
p.push_back(&v[0]);
printf("first node id : %d\n", (*p[0]).getN());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我将一个节点对象插入到全局向量中,并将该对象的指针插入到本地向量中.我上面代码的输出是:
first node id : 1
Run Code Online (Sandbox Code Playgroud)
但是,如果我将主要功能更改为:
int main()
{
vector<Node*> p;
v.push_back(Node(1));
p.push_back(&v[0]);
v.push_back(Node(2));
p.push_back(&v[1]);
printf("first node id : %d\n", (*p[0]).getN());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
代码打印垃圾值:
first node id : 32390176
Run Code Online (Sandbox Code Playgroud)
我无法弄清楚这个问题.vector插入后数据结构是否会更改每个对象的引用?我怎样才能解决这个问题 ?
编辑:我有很多答案告诉我,我应该将删除分成另一个循环.也许我没有说清楚,但我在上一段中说过,我想找到一个解决方法.即保持当前的代码结构,但使用一些鲜为人知的C++ fu来使其工作.
好吧,我知道调用erase()一个向量会使元素的迭代器和它之后的所有迭代器失效,并且erase()会将迭代器返回到下一个有效的迭代器,但是如果擦除发生在其他地方呢?
我有以下情况(简化):
警告:不要认为这是整个代码.下面显示的内容非常简单,以说明我的问题.下面显示的所有类和方法实际上要复杂得多.
class Child {
Parent *parent;
}
class Parent {
vector<Child*> child;
}
void Parent::erase(Child* a) {
// find an iterator, it, that points to Child* a
child.erase(it);
}
int Child::update() {
if(x()) parent.erase(*this) // Sometimes it will; sometimes (most) it won't
return y;
}
void Parent::update() {
int i = 0;
for(vector<A>::iterator it = child.begin(); it != child.end(); it++)
i += (*it)->update();
}
Run Code Online (Sandbox Code Playgroud)
因此,很明显,(*it)->update()如果x()返回true ,它会在运行后崩溃,因为当它执行时,Child会告诉Parent将它从向量中移除,使迭代器无效.
有没有什么方法可以解决这个问题,而不是让Parent::erase() …
std::list迭代器有一些非常好的属性 - 当删除任何其他元素,添加新元素时,甚至当交换2个列表时,它们仍然有效(迭代器失效规则)!
考虑以下代码行为并且迭代器是通过指向实际节点的指针的形式实现的,当移动列表时,该指针不会改变,我猜测迭代器在std::list移动a时在新容器中仍然有效,而且我可以通过访问实际具有"预期"值的无效内存进入UB区域.
std::list<int> l1{3, 2, 1};
std::list<int> l2;
auto it = std::prev(l1.end());
std::cout<<l1.size()<<" "<<l2.size()<<" "<<*it<<std::endl;
l2 = std::move(l1);
std::cout<<l2.size()<<" "<<*it<<std::endl;
3 0 1
3 1
Run Code Online (Sandbox Code Playgroud)
如果std::list移动时迭代器保持有效,它是否由标准保证?其他容器怎么样?
我需要根据插入时间(或其他更高效的东西)从std :: map中删除元素.
地图可能包含数千个元素,如果我存储时间并迭代地图以检查每个元素的时间,它可能最终会非常耗时.
有没有人知道如何在std :: map老化时擦除它们?
参考chetan.j9的http://www.careercup.com/question?id=17188673
void Insert( string s ) {
if( IsElementPresent(s) )
return;
myMap[s] = myMapVector.size();
unordered_map<string,int>::iterator it = myMap.find(s);
myMapVector.push_back(it);
}
Run Code Online (Sandbox Code Playgroud)
问题>我们可以存储unordered_map的迭代器以供以后检索吗?根据我的理解,插入或删除元素后迭代器将失效.
谢谢
对于std :: vector的副本分配,当源的大小小于目标的容量时,是否允许重新分配存储和容量缩减?或者是否保证不会发生重新分配/收缩(即始终遵守先前的保留())?
另一方面,如果源的大小大于目的地的容量并且重新分配,则需要重新分配是否与源的容量相关(例如,目的地的新容量应不小于源的容量,或者甚至需要它们是一样的)?或者重新分配只是完成其工作(基于新的大小)而不考虑源的容量?
至于移动分配,我认为不会进行存储重新分配(虽然我未能在标准中找到相关部分),那么它是否意味着目标新容量的值与源的旧容量完全相同?我能期望v = vector<T>{};产生同样的效果vector<T>{}.swap(v);吗?
我想答案都隐藏在标准的某个地方,但我却找不到它们.(如果C++ 11和C++ 03的内容不同,我想知道两者的各种要求.)
PS:对于上述问题的答案,对于std :: string是否相同(仅在C++ 11中,这意味着连续存储而且没有COW,C++ 03字符串不在雷达中)?