看看这段代码。
class Obj{
public:
int id;
Obj(int id) : id(id){}
};
int main()
{
std::vector<Obj> v;
for(int i=0; i<3; i++) // add some elements
v.push_back(Obj(i));
Obj& ref = v[2]; // grab references (two ways)
Obj* ref_bad = &v[2];
for(int i=3; i<100000; i++) // make the vector re-allocate
v.push_back(Obj(i));
std::cout << ref.id << std::endl; // prints 2
std::cout << (*ref_bad).id << std::endl; // prints 2, but always?
return 0;
}
在这两种情况下,这个简单的代码打印2
,但我不确定这是否是每次可能执行的行为。
由于向量在某一点重新分配(好吧,不一定总是,这取决于操作系统的段管理决策),这个打印不应该在某一点失败吗?
我在不同点使用printf
和%p
打印指针,但值不同,但我并不完全理解C ++真正引用的行为;这样做总是安全的吗? (对可能改变其内存位置的内容进行C ++引用)
答案 0 :(得分:3)
您的程序有不确定的行为。
在ref
上调用ref_bad
时, push_back()
和v
无效。
来自http://en.cppreference.com/w/cpp/container/vector/push_back:
如果新
size()
大于capacity()
,则所有迭代器和引用(包括过去的迭代器)都将失效。否则只有过去的迭代器无效。
答案 1 :(得分:2)
尽管有人可能会说,引用实际上是伪装指针的语法糖,但对于它们 1 ,它们完全没有魔力。
知道了这一点,很明显,如果你将指针(=引用)保留到重新分配的内存中,这样的指针(=引用)就会变得无效,因为它指向你不再拥有的内存,因此访问它是Undefined Behavior。
请注意,对于STL容器,这都是记录的 - 如果您查找std::vector<T>::push_back
,您会发现,如果新大小变得大于当前容量,它可能会使所有迭代器和引用无效。
这就是为什么通常在操作向量时保留索引,而不是迭代器或指针的原因。
const
引用可以绑定到tenporaries并使它们保持活动超出原始表达式的末尾(受某些规则约束)。