在下面的程序中,字符串被添加到空商店。然后,该存储元素的地址存储在指针's1'中。然后添加另一个字符串,这会导致指向原始元素的指针失败。
#include <iostream>
#include <string>
#include <vector>
class store2
{
public:
void add(std::string s) {words.push_back(s); last_added2 = &words.at(words.size() - 1);}
std::string* last_added() {return last_added2;}
private:
std::string* last_added2;
std::vector<std::string> words;
};
void main()
{
store2 store;
store.add("one");
std::string* s1 = store.last_added();
std::cout<<*s1<<std::endl;
store.add("two");
std::cout<<*s1<<std::endl; // crash
}
答案 0 :(得分:3)
向std::vector
添加新项时,向量可能需要扩展其缓冲区,并且通过这样做,它可能会将缓冲区移动到不同的内存区域。因此,指向其元素的指针变得无效。为了简化,在调整向量大小后,指向向量项的指针不能保证有效,如果向量没有足够的保留空间,push_back
可能会调整向量的大小。
您可以在开头为矢量预留空间,但是您可以对可以分配到矢量中的项目数量进行限制。
答案 1 :(得分:1)
如果您需要确保指向集合的指针仍然有效,您可能需要除向量之外的其他内容(例如,您可以使用std::deque
或std::list
代替{{1}通常在两者之间是首选的。)
或者,您可以返回字符串的索引,而不是返回指针(通常是一个糟糕的主意),并提供一个成员函数,该函数在使用时会转换为向量。
答案 2 :(得分:1)
你有什么特别的理由要使用指针(堆)吗? 如果没有,请执行:
class store2
{
public:
void add(std::string s) {words.push_back(s);}
std::string last_added() { if (words.size() == 0) return "";
return words[words.size()-1];}
private:
std::vector<std::string> words;
}
答案 3 :(得分:0)
std::vector
的迭代器在修改其内容时可能会失效。请参阅vector iterator invalidation。
如果你真的想保留现有的接口并保留插入向量的元素的指针,你可以按指针而不是按值存储字符串,例如:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class store2
{
public:
store2 ()
{
}
~store2 ()
{
for (std::vector<std::string *>::iterator it =
words.begin (), end_it = words.end ();
it != end_it; ++it)
{
delete *it;
}
words.clear ();
}
void add (const std::string & s)
{
std::auto_ptr<std::string> v (new std::string (s));
words.push_back (v.get ());
v.release ();
}
std::string *last_added ()
{
return words.back ();
}
const std::string *last_added () const
{
return words.back ();
}
private:
std::vector<std::string *> words;
};
int main ()
{
store2 store;
store.add("one");
std::string* s1 = store.last_added();
std::cout<<*s1<<std::endl;
store.add("two");
std::cout<<*s1<<std::endl; // no crash :-)
}
Boost中还有ptr_vector
类,旨在使这种解决方案更具可重用性和鲁棒性(即自动管理内存,因此您不必担心从向量中删除指针时删除字符串等)。