我试图迭代一个向量,保留上一次迭代的值的副本。令我惊讶的是,我的prev
指针最终指向循环的当前值。
#include <iostream>
#include <vector>
int main() {
std::vector<std::string> v;
v.push_back("one");
v.push_back("two");
v.push_back("three");
std::string *prev = nullptr;
for (std::string s : v) {
std::cout << "s: " << s << std::endl;
if (prev == nullptr) {
std::cout << "prev: (null)" << std::endl << std::endl;
} else {
std::cout << "prev: " << *prev << std::endl << std::endl;
}
prev = &s;
}
std::cout << "final prev: " << *prev << std::endl;
}
这是输出:
s: one
prev: (null)
s: two
prev: two
s: three
prev: three
final prev: three
这是为什么?什么是解决问题的正确方法?
答案 0 :(得分:5)
您正在std::string s
指针中存储std::string *prev
变量的地址。问题是,s
变量的值在每次迭代时都会更改为从v
提取的当前值,这就是为什么你的prev
指针指向循环的当前值的原因。
解决问题的方法是让您的prev
变量变为简单std::string
,并在每次迭代结束时为其分配s
。你会“按价值”保存。
答案 1 :(得分:3)
只需更改
for (std::string s : v) {
到
for (std::string& s : v) {
那样prev = &s;
将实际字符串的地址存储在向量中,而不是构造本地副本,然后获取该局部变量的地址。
详细说明:
在原始版本中,s
是一个本地std::string
变量,它在每次循环迭代中获取新的副本。然后在循环结束时,您将获取在wards之后直接销毁的局部变量的地址,从而在下一次迭代中访问该指针时导致悬空指针和未定义的行为。但是,因为s
通常会在每次循环迭代中的相同位置创建,所以未完成的行为将以prev
的形式实现,现在指向 new 局部变量已使用数组的下一个元素初始化的s
。因此,存储在prev中的地址将在下一个循环中有效,但它现在指向一个新创建的局部变量,该变量又是向量中下一个元素的副本。因此std::cout << "prev: " << *prev
通常会打印与std::cout << "s: " << s
相同的字符串。通过某些编译器优化,可以想象其他可能的行为。
现在使用新变体,s
又是一个局部变量,但现在它不是副本,而是对v中当前元素的引用。结果&s
现在返回向量中实际元素的地址 - 而不是局部变量 - 其内容在循环迭代之间不会改变,您可以按照预期的方式使用prev
。
答案 2 :(得分:2)
s
的范围一直持续到for
循环结束。如果存储指向s
的指针,则会在该迭代结束时悬空。当你取消引用这个悬空指针时,你有未定义的行为。实际上,您的实现在每次迭代中都会为s
重用相同的内存,因此您的指针总是恰好指向s
的当前值。
您根本无法保持指针/引用仅存在于循环的特定迭代中的某些内容,并期望它在下一次迭代中有效。在您的情况下,修复很简单:不要存储指针/引用 - 通过使prev
成为std::string
来存储值本身。或者,您可以指向std::vector
的原始元素,但这意味着将s
更改为std::string&
。
答案 3 :(得分:0)
因为您正在为正在更改的变量分配指针,并且当该变量也随着指针的变化而变化时也会发生变化。
这样可行:
std::string& prev = v.front();
for (std::string& s : v) {
std::cout << "s: " << s << std::endl;
std::cout << "prev: " << std::endl;
prev = s;
}
问题是,显然,在第一个周期,你的状态不一致。您应该从向量的第二个元素开始迭代,并以特殊方式处理向量的第一个元素。这可能是例如构建range view。
答案 4 :(得分:0)
string *prev = nullptr;
for(string &s : v) //note change
{
if(!prev)
cout<<s<<"-- null"<<endl;
else
cout<<s<<"--"<<*prev<<endl;
prev = &s;
}
cout << "final prev: " << *prev << endl;