我在http://developer-resource.blogspot.com.au/2009/01/pros-and-cons-of-returing-references.html遇到的博客写道:
在这个代码库工作了一段时间之后我相信了 返回引用是邪恶的,应该被视为 返回一个指针,这是避免它。
例如,出现需要一周时间调试的问题是 以下内容:
class Foo {
std::vector< Bar > m_vec;
public:
void insert(Bar& b) { m_vec.push_back(b); }
Bar const& getById(int id) { return m_vec[id]; }
}
此示例中的问题是客户端正在调用和获取 存储在向量中的引用。现在发生了什么 客户插入一堆新元素?向量需要调整大小 内部并猜测所有这些参考会发生什么?那是 对那里无效。这导致很难找到错误 通过删除&amp;。
简单地修复
我看不出代码有什么问题。我误解了参考文献和&amp; STL容器,或帖子不正确?
答案 0 :(得分:4)
例如,假设你在向量中有2个元素:
a
和b
。您可以返回这些r1
和r2
的参考文献。
现在另一个客户端插入向量。由于向量仅存在两个元素存储。它重新分配存储。它会复制a
和b
并在其后插入c
。这会更改a
和b
的位置。因此,引用r1
和r2
现在无效,并指向垃圾位置。
如果getById方法没有通过引用返回,那么就会复制一切并且一切都会正常工作。
答案 1 :(得分:4)
问题更容易显示为:
std::vector<int> vec;
vec.push_back(1);
const int& ref = vec[0];
vec.push_back(ref);
vec[1]
的内容未定义。在第二个push_back
中,ref
引用了内存vec[0]
初始化时的位置。在push_back
内,vector
可能需要重新分配,从而使ref
引用的内容无效。
这是一个很大的不便,但幸运的是,这不是经常发生的问题。 Foo
容器人是否插入了他们刚刚通过ID找到的Bar
?这对我来说似乎很有趣。在每次访问中发现副本似乎有点过头来解决问题。如果你觉得它够糟糕,
void insert(const Bar& b)
{
if ((m_vec.data() <= &b) && (&b < m_vec.data() + m_vec.size()))
{
Bar copy(b);
return insert(copy);
}
else
m_vec.push_back(b);
}
在C ++ 11中,如此编写Foo::insert
会好得多(假设Bar
有一个不错的移动构造函数):
void insert(Bar b)
{
m_vec.emplace_back(std::move(b));
}
答案 2 :(得分:2)
除了其他答案之外,值得指出的是,这种效果取决于容器类型。例如,对于vectors,我们有:
当成员函数必须增加时,会发生向量重新分配 包含在矢量对象中的序列超出其当前存储空间 容量。其他插入和擦除可能会改变各种存储 序列中的地址。在所有这些情况下,迭代器或 指向序列的改变部分的参考变为 无效。如果没有重新分配,只有迭代器和引用 在插入/删除点保持有效之前。
虽然lists由于存储数据的方式而在这方面更为宽松:
当成员函数必须插入或擦除时,会发生列表重新分配 列表的元素。在所有这些情况下,只有迭代器或引用 在受控序列的擦除部分的那一点变为 无效。
等其他容器类型。
答案 3 :(得分:0)
来自同一篇文章的评论:
这里的问题不是通过引用返回是邪恶的,就是这样 您返回的引用可能会变为无效。
第153页,第62节&#34; C ++标准库:教程和 参考&#34; - Josuttis,读到:
&#34;插入或删除elmeents会使引用,指针和 引用以下元素的迭代器。如果插入导致 重新分配,它使所有引用,迭代器和指针无效&#34;
您的代码示例与保存第一个代码示例一样邪恶 向量的元素,向量中插入1000个元素,和 然后尝试使用现有的参考。