防止矢量项被移动

时间:2017-02-16 17:50:32

标签: c++ pointers vector erase

对我来说这是一个学习问题,希望其他人也是如此。我的问题分解为指针指向向量的内容。当我擦除向量的第一个元素时会出现问题。我不太确定我在期待什么,我不知何故假设,当移除项目时,矢量不会开始在内存中移动对象。

我的问题是:有没有办法将对象保留在内存中?例如,更改向量的基础容器?在我的特定示例中,我将删除指针访问,并且只使用对象的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

3 个答案:

答案 0 :(得分:1)

虽然你无法达到你想要的目的,但有两种简单的选择。第一种方法是使用std::vector<std::unique_ptr<T>>代替std::vector<T>。向量调整大小时,不会移动每个对象的实际实例。这意味着将&aList[i]使用aList[i].get()aList[i].id更改为aList[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。您必须遍历列表才能获得指针。

答案 1 :(得分: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&lt; MagicPointerCore&gt;,MagicVector具有向量&lt; MagicPointerCore&GT; associated_pointers。

请注意,MagicVector的析构函数必须将其所有关联指针设置为无效,因为MagicPointer可以比其父级的范围更长。

答案 2 :(得分:-1)

  

我在期待,我以某种方式假设,在删除项目时,矢量不会开始在内存中移动对象。

怎么样?你还期待什么? std::vector保证内存中包含连续元素的连续序列。因此,如果删除某些内容,则需要在该连续内存中替换其他元素。