2017-04-19 18 views
2

看看這段代碼。這個std :: cout最終會打印垃圾還是意想不到的東西?

class Obj{ 
public: 
    int id; 
    Obj(int id) : id(id){} 
}; 

int main() 
{ 

    std::vector<Obj> v; 

    for(int i=0; i<3; i++) // add some elements 
     v.push_back(Obj(i)); 

    Obj& ref = v[2]; // grab references (two ways) 
    Obj* ref_bad = &v[2]; 

    for(int i=3; i<100000; i++) // make the vector re-allocate 
     v.push_back(Obj(i)); 

    std::cout << ref.id << std::endl; // prints 2 

    std::cout << (*ref_bad).id << std::endl; // prints 2, but always? 

    return 0; 
} 

這個簡單的代碼打印2在這兩種情況下,但我不知道這是否會爲每一個可能的執行行爲。

由於矢量在某一點重新分配(當然,不一定總是取決於操作系統的段管理決策),不應該在一個點上打印失敗嗎?

我在不同的點上使用printf%p打印指針,值不同,但我不完全理解C++的真實引用的行爲;這樣做總是安全的嗎? (使C++參考某些可能會改變其內存位置的東西)

+4

引用和指針變得懸空,所以行爲是不確定的。 – songyuanyao

回答

3

您的程序有未定義的行爲。

refref_badv上調用push_back()時失效。

http://en.cppreference.com/w/cpp/container/vector/push_back

如果新size()大於capacity()那麼所有迭代器和引用(包括過去的最末端迭代器)無效。否則只有最後一個迭代器失效。

+0

好的,在這種情況下,而不是引用/指針,只保留所需的項目的索引是最好的選擇? –

+0

@FeloVilches,是的。肯定。 –

2

儘管究竟是什麼人可能會說,引用是變相指針其實語法糖,有關於他們正好爲零魔法。知道了這一點,很明顯,如果你將一個指針(=引用)保存到被重新分配的內存中,這樣一個指針(=引用)將變得無效,因爲它指向你不再擁有的內存,因此訪問它是未定義的行爲。

請注意,對於STL容器,這是全部記錄 - 如果您查找std::vector<T>::push_back,您會發現,如果新大小大於當前容量,它可以使所有迭代器和引用無效。

這就是爲什麼通常在操縱向量時保留索引而不是迭代器或指針的原因。


  1. ,我能想到的引用的唯一法寶就是const引用可以綁定到tenporaries並讓他們活着超出了原來的表達式(受到某種規則)的結束。