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

C++ 包含引用或指针的对象向量,c++,pointers,vector,reference,C++,Pointers,Vector,Reference,我将一些对象存储在向量中。当我调用这样一个使用引用的对象的成员函数时,程序被终止(没有错误)。我写了下面的代码来运行一些测试。添加元素后,第一个条目中的引用失败。为什么会这样?我能做些什么来避免这个问题?当我使用指针而不是引用时,这是完全相同的行为 #include <iostream> #include <vector> using namespace std; class A{ public: A(int i) : var(i), ref(var) {}

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

#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;
} 

这是因为你调用emplace_back会导致向量调整大小。为了做到这一点,向量可能需要也可能不需要将整个向量移动到内存中的不同位置。您的“ref”仍在引用旧内存位置

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

标准文档中提到了:

迭代器有效性

如果发生重新分配,所有迭代器、指针 与此容器相关的引用将无效。否则,, 只有结束迭代器无效,所有其他迭代器无效, 元素的指针和引用保证不断引用 他们在通话前提到的相同元素

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

int& get_ref() {return var;}
。。。虽然我宁愿使用智能指针而不是这种东西

或者,正如RnMss建议的那样,实现复制构造函数,以便在通过向量复制对象时引用新位置:

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

好的,下面是发生的事情。它确实有助于从内存位置的角度理解对象,并记住允许向量在内存中移动对象

v.emplace\u back(5)

在向量中创建A对象。此对象现在位于从
0x1234
0x123C
的内存块中。成员变量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;}

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

因此,dani最好使用类似
int&get_ref(){return var;}
的方法,然后呢?(或者如果
const int&
会更好吗?我永远记不起返回成员变量引用的约定,尽管没有
const
,像
v.at(I).get_ref()=3
这样的事情成为可能。)关于迭代器有效性的摘录是关于向量元素引用的有效性。ref是指class@JAB是的,您的
int&get_ref(){return var;}
建议会更好。返回对成员变量的引用总是有点可怕;如果我必须这样做,我宁愿使用智能指针。重要的不是
emplace\u back
get\u ref()
。当重新分配发生时,会调用默认的复制构造函数,它不会按预期的方式初始化
ref
,而是复制旧对象的
var
的地址。实现显式复制/移动构造函数将解决此问题。@lizusek我不同意。“如果发生重新分配,则与此容器相关的所有…引用都将无效”<在他的示例中,code>ref是一个与容器相关的引用,因为它引用了容器中的一个结构成员。正在使用哪个编译器和版本?在处使用
也可能会导致问题,具体取决于哪个编译器。我在
上找不到讨论编译器有问题的帖子。如果有疑问,请使用
[]
运算符。在本例中,它看起来像
v[i]。get_var()
v[i]。get_ref()
@Edward为什么要添加C++11标记?C++标签不够吗?关于C++有什么特别的吗?嗨,我使用GCC 4.81.使用uf
[]
没有什么区别。很高兴看到使用了4.8.1编译器。应该是最近的事了,at的怪癖消失了。@Tom,
emplace\u back
只是C++11。
int & get_ref() {return var;}