简体   繁体   English

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

[英]Vector of objects containing references or pointers

I store some objects in a vector. 我将一些对象存储在向量中。 When I call a member function of such an object that uses a reference the program gets terminated (no error). 当我调用这样一个使用引用的对象的成员函数时,程序终止(没有错误)。 I wrote the following code do run some tests. 我写了下面的代码做了一些测试。 It seams like after adding elements, the reference in the first entry fails. 它在添加元素之后接缝,第一个条目中的引用失败。 Why is that and what can I do to avoid this issue? 为什么这样做,我该怎么做才能避免这个问题呢? It's exactly the same behaviour when I use pointers instead of references. 当我使用指针而不是引用时,它的行为完全相同。

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

The output is: 输出是:

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

It's because your calls to emplace_back are causing the vector to resize. 这是因为你对emplace_back的调用导致向量调整大小。 In order to do this, the vector may or may not have to move the entire vector to a different place in memory. 为了做到这一点,向量可能必须或可能不必将整个向量移动到存储器中的不同位置。 Your "ref" is still referencing the old memory location. 您的“ref”仍然引用旧的内存位置。

Whether or not this actually happens is somewhat implementation dependent; 这实际上是否发生在某种程度上依赖于实现; compilers are free to reserve extra memory for the vector so they don't have to reallocate every single time you add something to the back. 编译器可以自由地为向量保留额外的内存,这样他们就不必在每次向后面添加内容时重新分配。

It's mentioned in the standard documentation for emplace_back : 它在emplace_back的标准文档中提到

Iterator validity 迭代器有效性

If a reallocation happens, all iterators, pointers and references related to this container are invalidated. 如果发生重新分配,则与此容器相关的所有迭代器,指针和引用都将失效。 Otherwise, only the end iterator is invalidated, and all other iterators, pointers and references to elements are guaranteed to keep referring to the same elements they were referring to before the call. 否则,只有结束迭代器失效,并且所有其他迭代器,指针和对元素的引用都保证在调用之前引用它们所引用的相同元素。

To avoid the problem you could either (as JAB suggested in the comments) create the reference on the fly instead of storing it as a member variable: 为了避免这个问题,您可以(如评论中的JAB建议)动态创建引用,而不是将其存储为成员变量:

int& get_ref() {return var;}

... although I would much rather use a smart pointer instead of this sort of thing. ...虽然我宁愿使用智能指针而不是这种东西。

Or, as RnMss suggested, implement the copy constructor so that it references the new location whenever the object is copied by vector: 或者,正如RnMss建议的那样,实现复制构造函数,以便每当向量复制对象时它都引用新位置:

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

Okay, so here is what's happening. 好的,所以这里发生了什么。 It really helps to understand your objects in terms of memory location, and remember that vector is allowed to move objects around in memory. 根据内存位置来理解对象真的很有帮助,并且记住允许向量在内存中移动对象。

v.emplace_back(5)

You create an A-object in the vector. 您在向量中创建一个A对象。 This object now resides in a block of memory ranging from 0x1234 to 0x123C . 该对象现在驻留在从0x12340x123C的存储器块中。 Member variable var sits at 0x1234 and member variable ref sits at 0x1238 . 成员变量var位于0x1234 ,成员变量ref位于0x1238 For this object, the value of var is 0x0005 and the value of ref is 0x1234 . 对于此对象, var的值为0x0005ref的值为0x1234

While adding elements to the vector, the vector runs out of space during the second insert. 在向量中添加元素时,向量在第二次插入期间用完了空间。 So, it resizes and moves the current elements (which at this moment is just the first element) from location 0x1234 to location 0x2000 . 因此,它调整大小并将当前元素(此时只是第一个元素)从位置0x1234到位置0x2000 This means the member elements also moved, so var is now located at address 0x2000 and ref is now located at 0x2004 . 这意味着成员元素也移动了,因此var现在位于地址0x2000ref现在位于0x2004 But their values were copied, so the value of var is still 0x0005 and the value of ref is still 0x1234 . 但是它们的值被复制了,因此var的值仍然是0x0005 ,而ref的值仍然是0x1234

ref is pointing at an invalid location (but var still contains the right value!). ref指向一个无效的位置(但var仍然包含正确的值!)。 Trying to access the memory ref now points to undefined behavior and generally bad. 尝试访问内存引用现在指向未定义的行为,通常是坏的。

Something like this would be a much more typical approach to providing reference access to a member attribute: 这样的事情将是一种更典型的方法来提供对成员属性的引用访问:

int & get_ref() {return var;}

Having references as member attributes isn't wrong in and of itself, but if you are storing a reference to an object, you have to make sure that that object doesn't move. 将引用作为成员属性本身并没有 ,但如果要存储对象的引用,则必须确保该对象不会移动。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM