2014-03-26 86 views
2

我將一些對象存儲在向量中。當我調用這個使用引用的對象的成員函數時,程序終止(沒有錯誤)。我寫了下面的代碼做一些測試。它在添加元素之後會出現接縫,第一個入口中的引用失敗。爲什麼是這樣,我能做些什麼來避免這個問題?當我使用指針而不是引用時,這是完全一樣的行爲。包含引用或指針的對象的向量

#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 
+0

正在使用哪個編譯器和版本?使用'at'也可能會導致問題,具體取決於哪個編譯器。我一直沒有找到討論編譯器遇到'at'問題的帖子。如果有疑問,請使用'[]'運算符。在這種情況下,它看起來像'v [i] .get_var()'和'v [i] .get_ref()'。 –

+0

@愛德華你爲什麼添加C++ 11標籤? C++標籤不夠用 - 關於此問題有沒有特定於C++ 11的東西? – Tom

+0

嗨,我使用gcc 4.8.1。使用uf'[]'沒有區別。 – dani

回答

6

這是因爲你的來電emplace_back是造成向量來調整。爲了做到這一點,矢量可能或可能不必將整個矢量移動到內存中的不同位置。您的「參考」仍然參考舊的內存位置。

這是否真的發生有些實施依賴;編譯器可以自由地爲矢量預留額外的內存,所以他們不必每次重新分配給後面添加一些東西。

這是標準的文件中所提及的emplace_back

迭代器有效性

如果重新分配發生,所有迭代器,與此相關的容器指針 和引用無效。否則,僅 結束迭代器是無效的,和所有其他迭代器, 指針和引用元素保證讓指 他們的呼叫之前指的是相同的元素。

爲了避免這個問題,你既可以(作爲申訴委員會的意見提出)創建的,而不是把它作爲一個成員變量在運行過程中參考:

int& get_ref() {return var;} 

...雖然我更而是使用智能指針而不是這種東西。

或者,如RnMss建議,實現拷貝構造函數,以便它引用每當對象由向量複製的新位置:

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

那麼,使用'int&get_ref(){return var;}'這樣的話,那麼dani會更好呢? (或者更適合'const int&'?我永遠不會記住返回成員變量引用的約定,儘管沒有像'v.at(i).get_ref()= 3'那樣的'const'成爲可能。 ) – JAB

+0

關於迭代器有效性的摘錄是關於向量元素引用的有效性。 ref是類 – 4pie0

+0

的成員的引用@JAB是的,你對'int&get_ref(){return var;}'的建議會更好。返回對成員變量的引用總是有點可怕;如果我必須這樣做,我寧願使用智能指針。 –

3

好了,所以這裏發生了什麼。從內存位置的角度來理解你的對象是非常有幫助的,並且記住允許向量在內存中移動對象。

v.emplace_back(5)

創建在載體中的A-對象。該對象現在駐留在範圍從0x12340x123C的一塊內存中。成員變量變種坐在0x1234和成員變量REF坐在0x1238。對於該目的,VAR的值是0x0005REF的值是0x1234

雖然將元素添加到載體中,載體中的第二插入過程中運行的空間。因此,它將位置0x1234中的當前元素(此時僅爲第一個元素)調整大小並將其移動到位置0x2000。這意味着成員元素也移動了,所以變量現在位於地址0x2000參考現在位於0x2004。但是它們的值被複制,所以var的值仍然是0x0005而且值ref仍然是0x1234

ref指向無效位置(但var仍包含正確的值!)。試圖訪問內存ref現在指向未定義的行爲,通常不好。

像這樣的事情將是提供給成員屬性引用訪問一個更典型的做法:

int & get_ref() {return var;} 

有引用爲成員的屬性是不是本身並,但如果你是存儲對象的引用時,必須確保該對象不移動。

相關問題