我有一个 std::vector<int>
和一个指向向量中一个元素的指针int*
。假设指针指向第三个元素:pointer=&vector.at(2)
。如果我现在打乱了这个向量,指针还会指向同样的元素(第三个元素)吗?还是它将指向现在已经移动了的原来是第三个元素的新位置?
之后,我想让问题更加一般化:当向量扩展或缩小时,向量元素的指针和迭代器的行为如何?
我有一个 std::vector<int>
和一个指向向量中一个元素的指针int*
。假设指针指向第三个元素:pointer=&vector.at(2)
。如果我现在打乱了这个向量,指针还会指向同样的元素(第三个元素)吗?还是它将指向现在已经移动了的原来是第三个元素的新位置?
之后,我想让问题更加一般化:当向量扩展或缩小时,向量元素的指针和迭代器的行为如何?
指针将继续指向同一个位置,因此当您洗牌时,它将指向已移动到您指定位置的任何元素。
当您扩展向量的大小时,所有现有的指针和迭代器都可能变得无效。当你洗牌时,它们仍然指向同一个位置,该位置通常会包含不同于洗牌之前的值。
缩小向量的大小将取决于您如何执行此操作。一种方法是创建一个临时向量作为当前向量的副本,交换两个向量,然后销毁临时向量(通常通过让它超出范围来隐式销毁)。如果您这样做,指针将指向临时向量,并在其被销毁时无效。
如果您使用shrink_to_fit
,那么它(可能)不会使迭代器/指针无效,但可能没有任何效果(标准规定它是一个非绑定请求,并且不说它使迭代器/指针无效)。
shrink_to_fit
? - T.C.shrink_to_fit
使迭代器失效的权限。 - Jerry Coffinshrink_to_fit
。指向删除的元素的迭代器/指针显然是无效的。std::vector<T>::iterator
可以是 T *
的 typedef)。引用也是指针的伪装。#include <iostream>
#include <algorithm>
static void print_vec(const std::vector<int>& v) {
for (int i = 0; i < v.size(); ++i) {
std::cout << "Value: " << v.at(i) << " Address: " << &v.at(i) << std::endl;
}
}
static void shuffle_vec(std::vector<int>& v) {
auto engine = std::default_random_engine{};
std::shuffle(v.begin(), v.end(), engine);
}
int main() {
std::vector<int> v{1, 2, 3, 4, 5};
std::cout << "Before Shuffle: " << std::endl;
print_vec(v);
shuffle_vec(v);
std::cout << "After Shuffle: " << std::endl;
print_vec(v);
return 0;
}
输出:
Before Shuffle:
Value: 1 Address: 0x16eb320
Value: 2 Address: 0x16eb324
Value: 3 Address: 0x16eb328
Value: 4 Address: 0x16eb32c
Value: 5 Address: 0x16eb330
After Shuffle:
Value: 3 Address: 0x16eb320
Value: 1 Address: 0x16eb324
Value: 5 Address: 0x16eb328
Value: 4 Address: 0x16eb32c
Value: 2 Address: 0x16eb330
.erase
一个元素,则会使该位置及之后的每个迭代器失效。std::shuffle
不会使任何迭代器失效。它只是改变存储在那里的值。因此,第 n 个元素的指针在实践和理论上仍将指向第 n 个元素。.capacity()
之外,则所有迭代器都将失效。在实践中,它实际上将数据移动到新位置,指针现在是悬空指针。.erase(it)
或 .erase(a,b)
)时,所有第一个参数及之后的迭代器都将失效。这意味着对这些元素的引用和指针也失效了。在实践中,它们现在将指向链条“更下面”的元素(如果存在这样的元素),因为 .erase
都不会导致缓冲区重新分配,但标准不能保证这一点。.shrink_to_fit()
可以,vector<X>(vec).swap(vec)
(C++03 版本的 shrink-to-fit)也可以,以及增加大小超过 .capacity()
的操作。.capcity()
改变的操作实际上会使您的指针在实践和理论上都失效(或使指针指向超出末尾的位置)。vec.erase(vec.begin())
使向量中任何元素的所有指针无效。但实际上,它们仍然指向元素,但标准并没有讨论“现在指向下一个元素”的半无效状态。除了使用一种病态语言实现来标记指针失效外,我想不到会破坏它的实现方式。 - Yakk - Adam Nevraumonthttp://en.cppreference.com/w/cpp/container/vector
std::vector
是一个序列容器,封装了动态大小的数组。
元素被连续存储,这意味着元素不仅可以通过迭代器访问,还可以使用指向元素的普通指针的偏移量进行访问。迭代器 随机访问迭代器
迭代器失效
swap, std::swap
永不失效
shrink_to_fit
总是失效
resize
如果向量更改了容量,则所有元素都会失效。 如果没有更改,则仅在插入点之后的元素失效。
http://en.cppreference.com/w/cpp/container/vector/resize
当调整大小到更小的尺寸时,向量容量不会减少,因为这样会使所有迭代器无效,而不仅仅是等效的
pop_back()
调用将会使它们无效的那些迭代器。
vector::shuffle
之后,迭代器/指针不变,但解除引用会得到新值。
因为shuffle
使用swap
,它不会改变迭代器:
http://en.cppreference.com/w/cpp/algorithm/random_shuffle
template< class RandomIt, class URNG > void shuffle( RandomIt first, RandomIt last, URNG&& g );
RandomIt must meet the requirements of ValueSwappable and RandomAccessIterator.
http://en.cppreference.com/w/cpp/concept/Iterator
迭代器是其他迭代器类型的基本概念:输入迭代器、输出迭代器、正向迭代器、双向迭代器和随机访问迭代器。迭代器可以被看作指针的抽象。 [...]
`- 类型为It的左值满足可交换性 [...]
http://en.cppreference.com/w/cpp/concept/ValueSwappable
如果满足以下条件,类型T就是ValueSwappable: 1. 类型T符合迭代器要求 2. 对于任何类型为T的可解引用对象x(即除了end迭代器之外的任何值),*x都符合Swappable要求。http://en.cppreference.com/w/cpp/concept/Swappable
using std::swap; swap(u, t);
After the call, the value of t is the value held by u before the call, and the value of u is the value held by t before the call.
正如其他人所说,指针“指向”内存中的一个位置,而不管那里的内容是什么。实际上您可以做一些非常有趣的事情,比如有一个包含5个元素的数组,但打印出第6个位置的值,即使它不在数组的范围内。当您像array [5]这样访问一个长度为5的数组时,您将得到未定义的行为,这意味着可能会发生各种各样的事情,每次运行都可能返回完全不同的结果。请查看philipxy在下面提供的一些非常有用的链接来深入探讨这个概念。
因此,现在有一小段代码供您测试以实际看到这种效果。
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> values (5);
for (int i = 0; i < 5; i++)
values[i] = i;
for (int i = 0; i < 5; i++)
cout << values[i] << " ";
//Initialise the pointer so that it is pointing at the first element in vector
int* pointer = &values[0];
//By incrementing, we expect it to be pointing at the second element, which should be 1
pointer++;
cout << endl << "Pointer " << *pointer << endl;
//Reverse the order of the vector
reverse(values.begin(), values.end());
for (int i = 0; i < 5; i++)
cout << values[i] << " ";
cout << endl << "Pointer " << *pointer << endl;
return 0;
}
编辑:我刚刚了解到,C++标准确实比我想象得严格得多(感谢philipxy)。它确实规定向量必须在内部像C数组一样运行。请参见
http://herbsutter.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/
“因此,如果你的实现符合至少C++03标准,就不要考虑其他的了。例如,如果你将std::vector实现为一个链表(不太可能),那么洗牌、缩小大小等操作将不起作用。如果“std::vector”在内部使用类似于“int []”来存储其元素(很可能),那么重新排列元素可能意味着您的指针现在指向的值与之前不同(Stevo试过)。如果在这种情况下调整向量的大小,则再次完全取决于内部实现。在这种情况下,调整大小可能会分配一个新的“int []”,并复制旧内容。在这种情况下,您的指针将指向未分配的内存,因此可能会发生所有混乱。如果你幸运的话(取决于实现),那么通过“小”的数量缩小或增大向量可能什么都不会发生(你的指针仍然有效)。总结:不要这样做;-)(使用指针,然后修改容器)。”