不明白迭代器,引用和指针失效,一个例子



我读过很多关于引用、指针和迭代器无效的帖子。例如,我读到插入会使对deque元素的所有引用无效,那么为什么在下面的代码中我没有错误呢?

#include <deque>
int main()
{
std::deque<int> v1 = { 1, 3, 4, 5, 7, 8, 9, 1, 3, 4 };
int& a = v1[6];
std::deque<int>::iterator it = v1.insert(v1.begin() + 2, 3);
int c = a;
return a;
}

当我运行这个时,结果是9,所以"a"仍然指正确的元素。总的来说,我没有设法得到无效的错误。我尝试了不同的容器,甚至使用了指针和迭代器。

有时,的操作可能会使某些东西无效,而则不会。

我对std::deque实现还不够熟悉,无法对此发表评论,但如果你对std::vector进行了push_back,例如,你可能会使所有迭代器、引用和指向向量元素的指针无效,例如,因为std::vector需要分配更多内存来容纳新元素,最终将所有数据移动到新位置,内存可用的位置。

或者,您可能会使nothing无效,因为向量有足够的空间在适当的位置构建一个新元素,或者足够幸运地在其当前内存位置的末尾获得了足够的新内存,并且在仍然更改大小的情况下不必移动任何东西。

通常,文档会仔细记录哪些操作会使哪些操作无效。例如,在中搜索"无效"https://en.cppreference.com/w/cpp/container/deque。

此外,标准数据结构的特定实现可能比标准保证更安全,但依赖这一点会使您的代码高度不可移植,并可能在未言明的安全保证发生变化时引入隐藏的错误:一切似乎都很好,直到它不起作用。

唯一安全的做法是仔细阅读规范,当它不能保证这一点时,永远不要依赖于不会失效的东西。

此外,正如Enrico所指出的,你可能会遇到引用/指针/迭代器无效的情况,但从中读取会产生一个看起来很好的值,所以这样一个简单的测试方法是无效的

下面的代码,在我的系统上,显示了未定义行为的影响。

#include <deque>
#include <iostream>
int main()
{
std::deque<int> v1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
for (auto e : v1) std::cout << e << ' ';
std::cout << std::endl;
int& a = v1[1];
int& b = v1[2];
int& c = v1[3];
std::cout << a << ' ' << b << ' ' << c << std::endl;
std::deque<int>::iterator it = v1.insert(v1.begin() + 2, -1);
for (auto e : v1) std::cout << e << ' ';
std::cout << std::endl;
v1[7] = -3;
std::cout << a << ' ' << b << ' ' << c << std::endl;
return a;
}

它的输出对我来说是:

1 2 3 4 5 6 7 8 9 10 
2 3 4
1 2 -1 3 4 5 6 7 8 9 10 
-1 3 4

如果引用abc仍然有效,那么最后一行应该是

2 3 4

请不要由此推断a已失效,而bc仍然有效。它们全部无效。

试试看,也许你很"幸运",它也会向你展示同样的东西。如果没有,那么就利用容器中的元素数量和一些插入。在某个时刻,也许你会看到一些奇怪的东西,就像我的情况一样。

附录

std::deque的实现方式都使失效机制比"更简单"的std::vector更复杂。你也没有那么多方法来检查某件事是否真的会受到未定义行为的影响。例如,使用std::vector,您可以判断未定义的行为是否会在push_back上刺痛您;实际上,您有成员函数capacity,它告诉容器是否已经有足够的空间来容纳通过push_back插入更多元素所需的更大的size。例如,如果size给出8,capacity给出10,则可以"安全"地再push_back两个元素。如果您再推一个,则必须重新分配阵列。

最新更新