2017-02-16 49 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可以超出其父项的范围。