包含引用或指针的对象向量

时间:2014-03-26 18:09:58

标签: c++ pointers vector reference

我将一些对象存储在矢量中。当我调用这样一个使用引用的对象的成员函数时,程序终止(没有错误)。我写了下面的代码做了一些测试。它在添加元素之后接缝,第一个条目中的引用失败。为什么这样做,我该怎么做才能避免这个问题呢?当我使用指针而不是引用时,它的行为完全相同。

#include <iostream>
#include <vector>
using namespace std;

class A{
public:
    A(int i) : var(i), ref(var) {}
    int get_var() {return var;}
    int get_ref() {return ref;}
private:
    int var;
    int& ref;
};

int main ()
{
    vector<A> v;
    for(unsigned int i=0;i<=2 ;i++){
        v.emplace_back(i+5);
        cout<<"entry "<<i<<":"<<endl;
        cout<<"  var="<<v.at(i).get_var()<<endl;
        cout<<"  ref="<<v.at(i).get_ref()<<endl;
    }
    cout<<endl;

    for(unsigned int i=0;i<=2 ;i++){
        cout<<"entry "<<i<<":"<<endl;
        cout<<"  var="<<v.at(i).get_var()<<endl;
        cout<<"  ref="<<v.at(i).get_ref()<<endl;
    }
    return 0;
} 

输出结果为:

entry 0:
  var=5
  ref=5
entry 1:
  var=6
  ref=6
entry 2:
  var=7
  ref=7

entry 0:
  var=5
  ref=0          /////////////here it happens!
entry 1:
  var=6
  ref=6
entry 2:
  var=7
  ref=7
v has 3 entries

2 个答案:

答案 0 :(得分:6)

这是因为你对emplace_back的调用导致向量调整大小。为了做到这一点,向量可能必须或可能不必将整个向量移动到存储器中的不同位置。您的“ref”仍然引用旧的内存位置。

这是否真的发生在某种程度上依赖于实现;编译器可以自由地为向量保留额外的内存,这样他们就不必在每次向后面添加内容时重新分配。

emplace_back的标准文档中提到了它:

  

迭代器有效期

     

如果发生重新分配,则所有迭代器,指针   与此容器相关的引用无效。除此以外,   只有结束迭代器失效,以及所有其他迭代器,   保证指针和元素引用不断引用   在电话会议之前他们指的是相同的元素。

为了避免这个问题,您可以(如评论中的JAB建议)动态创建引用,而不是将其存储为成员变量:

int& get_ref() {return var;}

...虽然我宁愿使用智能指针而不是这种东西。

或者,正如RnMss建议的那样,实现复制构造函数,以便每当对象被vector复制时它引用新位置:

A(A const& other) : ref(var) {
    *this = other;
}

答案 1 :(得分:3)

好的,所以这里正在发生的事情。根据内存位置来理解对象真的很有帮助,并且记住允许向量在内存中移动对象。

v.emplace_back(5)

在向量中创建A对象。此对象现在位于从0x12340x123C的内存块中。成员变量 var 位于0x1234,成员变量 ref 位于0x1238。对于此对象, var 的值为0x0005 ref 的值为0x1234

在向量中添加元素时,向量在第二次插入期间用完了空间。因此,它调整大小并将当前元素(此时只是第一个元素)从位置0x1234移动到位置0x2000。这意味着会员元素也会移动,因此 var 现在位于地址0x2000 ref 现在位于0x2004。但是他们的值被复制了,因此 var 的值仍为0x0005,而 ref 的值仍为0x1234

ref 指向无效位置(但 var 仍包含正确的值!)。尝试访问内存 ref 现在指向未定义的行为,通常不好。

这样的事情是提供对成员属性的引用访问的一种更典型的方法:

int & get_ref() {return var;}

将引用作为成员属性本身并不是错误,但如果要存储对象的引用,则必须确保该对象不会移动。