如何使指针/引用的元素在向量上

How to make pointer/reference on element in vector?

本文关键字:元素 向量 引用 何使 指针      更新时间:2023-10-16

我有两个向量,对于一些元素(不是全部)我需要它们连接~如果我在一个向量中删除/更改这个元素,它应该在两个向量中删除/更改。

类似于指针:

int r = 10;
int *p= &r;
*p = 3;

例如在下一个代码中,它应该改变myvector2[0]。A到7.

#include <iostream>
#include <vector>
using namespace std;
struct elt {
    int a, b, c;
};
int main()
{
    vector<elt> myvector;
    vector <elt> myvector2;
    elt elt1 = {1,3,3};
    myvector.push_back(elt1);
    elt *elt2 = &elt1;
    myvector2.push_back(*elt2);
    myvector[0].a=7;
    cout << myvector[0].a << endl; //7
    cout << myvector2[0].a << endl; //1
    return 0;
}

我怎样才能使这成为可能?请帮助! !

正如tgmath所解释的,问题是在vector中最终会有两个不同的对象,因为标准容器是按值存储元素的。我建议您使用共享指针来适当地维护对象的生命周期(注意这使用了c++ 11的特性):

#include <iostream>
#include <vector>
#include <memory>
struct elt {
    int a, b, c;
};
int main()
{
    std::vector<std::shared_ptr<elt>> myvector;
    std::vector<std::shared_ptr<elt>> myvector2;
    auto elt1 = std::make_shared<elt>(elt {1,3,3});
    myvector.push_back(elt1);
    myvector2.push_back(elt1);
    myvector[0]->a=7;
    std::cout << myvector[0]->a << std::endl; //7
    std::cout << myvector2[0]->a << std::endl; //7
    return 0;
}

指向std::vector元素的指针和引用(以及迭代器)在向量重新分配时失效,这可能发生在插入过程中。因此,只有在保证向量在指针/引用的生命周期内不会重新分配时,才能保留这些值。如果不插入vector,或者在开始之前(在获取指针/引用/迭代器之前)对vector调用reserve(),扩展vector的容量,这样就不需要重新分配空间。

如果你不能保证这一点,你唯一的选择是保留索引而不是指针/引用。当然,你还需要访问vector本身来实现这一点,但是你应该能够保留一个指向它的指针或引用,例如

typedef std::pair<std::vector<elt>*, size_t> ReferenceIntoVector;

myvector2.push_back(*elt2);*elt2的副本添加到myvector2

这意味着两个向量都有自己的elt对象副本。它们都不同于elt1

如果你改变了第一个向量中的一个,第二个向量根本不改变。为此,您需要std::vector<elt*>在不同的向量中具有指向同一对象的不同指针。

首先,如果碰巧从另一个vector中删除了一个元素,则没有内置的方法自动将其从一个vector中删除。

在我看来,这里有两种不同的挑战:

  1. 如何更新一个列表所指向的元素,使其在另一个列表中更新?
  2. 如何从一个列表中删除元素,并从另一个列表中删除该元素?

第一个问题已经在其他帖子中得到了回答:使用std::共享指针而不是本地指针。它们同样快,并且会为你处理所有的内存管理。

请注意:这种方法只在您只更改共享指针所指向的元素的值时才有效,而不是共享指针所指向的

换句话说/代码:

std::vector<std::shared_ptr<elt>> vec1, vec2;
// Insert some elements
vec1.push_back( std::make_shared( elt{ 1, 2, 3} );
vec2.push_back( vec1.back() );
vec1[0]->x = 5;                     // OK, updated in both vectors.
vec1[0] = make_shared(elt {3,2,1}); // Error: vec1[0] is pointing to a new object.

另一个挑战要困难得多,如何从两个向量中自动删除元素。简短的回答是"你不能",长一点的回答是"你可以但不能直接"。

方法1:设置已删除的元素为nullptr .

这种方法有点脏,我不推荐这种方法,因为它需要所有使用vector的代码来检查元素是否为nullptr

// Encapsulate the object inside a `std::unique_ptr`
std::vector< std::shared_ptr< std::unique_ptr< elt >>> vec1, vec2;
// Adding elements are done similarly as before
vec1.push_back( std::make_shared( std::make_unique( elt{ 1, 2, 3} )));
vec2.push_back( vec1.back() );
// Now to delete a element you would do as follows
vec1[0]->reset(nullptr);          // Flag this element as deleted
vec1[0]->erase( vec1.begin() );   // Remove element from vec1
// Now let us assume we are iterating through the other vector at a later time:
for (auto it = vec2.begin(); it != vec2.end(); ++it ) { // Using iterators makes the code cleaner.
    if ( **it == nullptr ) { // If the unique_ptr == nullptr
       it = erase(it);
    else {
       etl & the_value = ***it; // Yes you need all three, one for the iterator, one for the shared_ptr and one for the unique_ptr...
    }
}

正如你所看到的,这很快就变得复杂了。但如果这就是你所需要的,它可以工作。

方法2:(在我看来更好)是使用观察者/观察设计模式。

为了实现此模式,您需要将vector转换为另一个类。让我们举一个简单的例子:

// First, create an interface describing all operations you want to be
// informed about.
template<class T>
class SharedElementListObserver {
protected:
    void elementDeleted( const shared_ptr<T> & elem ) = 0;
}
template<class T>
class SharedElementList : public SharedElementListObserver<T> {
   std::vector<std::shared_ptr<T>> data;
   std::unordered_set<SharedElementListObserver*> observers;
public:
   void push_back( const T & elem ) { data.push_back( std::make_shared<T>( elem )); }
   void push_back( std::shared_ptr &sptr ) { data.push_back( sptr ); }
   shared_ptr<T> operator[] (int index) { 
       return data[index]; 
   }
   shared_ptr<const T> operator[] (int index ) const { 
       return std::static_pointer_cast<const T>( data[index] ); 
   }
   // This will cause all instances of elem in all lists
   // linked either directly and indirectly to this 
   // to be removed.
   void delete( int idx ) {
        if (idx >= 0 && idx < data.size() ) {
             shared_ptr<T> temp = data[idx];
             data.erase( data.begin() + idx );
             for (auto observer : observers) {
                 observer->elementDeleted(temp);
             }
         }
   }
   // Link another list to this one to listen to deletions.
   void link( SharedElementListObserver* observer ) {
        if (observer == this) return;
        else if (observers.insert(observer).second) {
            observer->observers.insert(this);
        }
   }
   // Unlink previously linked observer.
   void unlink(SharedElementListObserver* observer) {
       observer->observers.erase(this);
       this->observers.erase(observer);
   }
protected:
   void elementDeleted( shared_ptr<T> & elem ) {
        for (int i = 0; i < data.size(); ) {
            if (data[i] == elem)
                delete(i);  // Not infinite loop, because of test above.
            else
                i++;
        }
   }

   // You also need to write an operator=, a copy-constructor and a destructor
   // that ensures that there are no dead observers in the observers list.
};

根据你的假设,这个类可以用许多不同的方式实现。有些可能比我刚才做的更简单。

以上代码如有错误,请告知。