2017-02-16 52 views
3

這對我和其他人都是一個學習問題。我的問題是有一個指向矢量內容的指針。當我擦除矢量的第一個元素時,會發生該問題。我不太清楚我期待的是什麼,我以某種方式假定,當刪除項目時,向量不會開始移動內存中的對象。防止矢量物品被移動

我的問題是:有沒有辦法將對象保留在內存中?例如更改矢量的底層容器?以我的特殊例子來說,我將刪除指針訪問權限,並僅僅使用和id作爲對象,因爲類無論如何都需要一個ID。

這裏是一個簡單的例子:

#include <iostream> 
#include <vector> 

class A 
{ 
public: 
    A(unsigned int id) : id(id) {}; 
    unsigned int id; 
}; 

int main() 
{ 
    std::vector<A> aList; 

    aList.push_back(A(1)); 
    aList.push_back(A(2)); 

    A * ptr1 = &aList[0]; 
    A * ptr2 = &aList[1]; 

    aList.erase(aList.begin()); 

    std::cout << "Pointer 1 points to \t" << ptr1 << " with content " << ptr1->id << std::endl; 
    std::cout << "Pointer 2 points to \t" << ptr2 << " with content " << ptr2->id << std::endl; 
    std::cout << "Element 1 is stored at \t" << &aList[0] << " with content " << aList[0].id << std::endl; 

} 

我得到的是:

Pointer 1 points to  0xf69320 with content 2 
Pointer 2 points to  0xf69324 with content 2 
Element 1 is stored at 0xf69320 with content 2 
+4

從矢量(以及許多其他操作)中刪除項目可能會在內存中移動內容。如果您想避免這種情況,請使用基於節點的容器,如std :: list或std :: deque。 –

+1

a矢量元素根據定義存儲在連續的存儲器中。如果你想要一個容器來保存一個元素,但記得從容器中移除它,那麼這不是一個矢量 – user463035818

+0

如果你真的想要一個這樣的矢量,然後使用std :: vector :: reserve(),它會保留一個空間,所以當您添加一個新的空間時,它會將該元素置於該預留空間,因此不會發生重新分配。只有當它需要增加尺寸時,矢量纔會重新分配 –

回答

-1

我期待,我莫名其妙地認爲,刪除項目時,該向量不會開始移動內存中的對象。

是怎麼回事?你還期望什麼? A std::vector保證它在內存中的連續系列元素。因此,如果刪除了某些內容,則需要在該連續內存中替換其他元素。

+0

這也是我想到的。我不知道從哪裏來。有沒有一個STL對象來代替圍繞保持地址的對象組織一個列表? –

+0

@Incomputable通常'std :: deque'不會好。你需要一個基於節點的容器,比如'std :: list'。 – NathanOliver

+0

@ruhigbrauner如果你想保留向量,你可以將元素存儲在一個向量中,但通過第二個向量來訪問它們,該向量保存指向第一個向量的指針。您可以從第二個向量中刪除元素,同時保持第一個元素不可變。也許不是最好的解決方案,但是我想到了 – user463035818

1

雖然你不能完全達到你想要的,但有兩個簡單的選擇。首先是使用std::vector<std::unique_ptr<T>>而不是std::vector<T>。當矢量調整大小時,每個對象的實際實例將不會移動。這意味着更改&aList[i]aList[i].get()aList[i].idaList[i]->id的任何用途。

#include <iostream> 
#include <memory> 
#include <vector> 

class A 
{ 
public: 
    A(unsigned int id) : id(id) {}; 
    unsigned int id; 
}; 

int main() 
{ 
    std::vector<std::unique_ptr<A>> aList; 

    aList.push_back(std::make_unique<A>(1)); 
    aList.push_back(std::make_unique<A>(2)); 

    A * ptr1 = aList[0].get(); 
    A * ptr2 = aList[1].get(); 

    aList.erase(aList.begin()); 

    // This output is undefined behavior, ptr1 points to a deleted object 
    //std::cout << "Pointer 1 points to \t" << ptr1 << " with content " << ptr1->id << std::endl; 
    std::cout << "Pointer 2 points to \t" << ptr2 << " with content " << ptr2->id << std::endl; 
    std::cout << "Element 1 is stored at \t" << aList[0].get() << " with content " << aList[0]->id << std::endl; 

} 

注意ptr1將指向被刪除的對象,因此它仍然是不確定的行爲,尊重它。

另一種解決方案可能是使用不會導致引用和指針無效的不同容器。 std::list永遠不會使節點無效,除非它被特別擦除。但是,隨機訪問不受支持,因此您的示例不能直接修改爲使用std::list。你將不得不遍歷列表來獲取你的指針。

+0

這會幫助,但我不能這樣做在我的用例。必須防止動態分配。 :( –

+0

'std :: array'或者'const std :: vector'可能是你最好的選擇然後 –

+0

關於列表方法:可以列表預留空間嗎? –

0

不知道這是否是你想要的,但這個怎麼樣:

(只有基本的佈局,您需要填寫的細節,也:沒有測試的設計,可能有某種缺陷)

template <class T> 
class MagicVector { 

    class MagicPointer { 

     friend class MagicVector; 

     private: 

     MagicVector* parent; 
     unsigned int position; 
     bool valid; 

     MagicPointer(MagicVector* par, const unsigned int pos); //yes, private! 

     public: 

     ~MagicPointer(); 

     T& content(); 
     void handle_erase(const unsigned int erase_position); 
    } 

    friend class MagicPointer; 

    private: 

    vector<T> data; 
    vector<std::shared_ptr<MagicPointer> > associated_pointers; 

    public: 

    (all the methods you need from vector) 
    void erase(const unsigned int position); 

    std::shared_ptr<MagicPointer> create_pointer(const unsigned int position); 

} 

template <class T> 
void MagicVector<T>::erase(const unsigned int position){ 
    data.erase(position); 
    for(unsigned int i=0; i<associated_pointers.size(); i++){ 
     associated_pointers[i].handle_erase(position); 
    } 
} 

template <class T> 
std::shared_ptr<MagicPointer> MagicVector<T>::create_pointer(const unsigned int position){ 

    associated_pointers.push_back(std::shared_ptr<MagicPointer>(new MagicPointer(this, position))); 
    return std::shared_ptr<MagicPointer>(associated_pointers.back()); 
} 

template <class T> 
MagicVector<T>::MagicPointer(MagicVector* par, const unsigned int pos){ 
    parent = par; 
    position = pos; 
    if (position < parent->data.size()){ 
     valid = true; 
    }else{ 
     valid = false; 
    } 
} 

template <class T> 
T& MagicVector<T>::MagicPointer::content(){ 
    if(not valid){ 
     (handle this somehow) 
    } 
    return parent->data[position]; 
} 

template <class T> 
void MagicVector<T>::MagicPointer::handle_erase(const unsigned int erase_position){ 
    if (erase_position < position){ 
     position--; 
    } 
    else if (erase_position == position){ 
     valid = false; 
    } 
} 

template <class T> 
MagicVector<T>::MagicPointer::~MagicPointer(){ 
    for(unsigned int i=0; i<parent->associated_pointers.size(); i++){ 
     if(parent->associated_pointers[i] == this){ 
      parent->associated_pointers.erase(i); 
      i=parent->associated_pointers.size(); 
     } 
    } 
} 

基本思想:你有自己的向量和指針的類,指針存儲向量中的位置。該矢量知道它是指針,並且只要有東西被擦除就相應地處理它們。

我並不完全滿意自己,那個通過MagicPointer的shared_ptr看起來很醜,但不知道如何簡化它。也許我們需要與MagicVector,MagicPointerCore以及MagicPointer這三個類一起工作:public shared_ptr < MagicPointerCore>,MagicVector的矢量爲< MagicPointerCore> associated_pointers。

請注意,MagicVector的析構函數必須將其所有關聯的指針設置爲無效,因爲MagicPointer可以超出其父項的範圍。