В C ++, как я могу получить указатель на вектор? - PullRequest
3 голосов
/ 14 апреля 2009

Я пишу некоторый код C ++, который манипулирует множеством векторов, которые меняются в размере и, таким образом, постоянно перераспределяются.

Я хотел бы получить указатель на эти векторы, который остается действительным даже после перераспределения вектора. Точнее говоря, я просто хочу, чтобы эти «указатели» запомнили, на какой вектор они указывают и индекс, на который они указывают. Когда я разыменовываю их, используя стандартный синтаксис (* ptr), я просто хочу, чтобы они выполнили очевидный поиск.

Очевидно, что действительные указатели не будут действительны после перераспределения, и я понимаю, что итераторы также недействительны после перераспределения. Также обратите внимание, что мне все равно, вставляются ли элементы перед моими объектами, поэтому эти «указатели» действительно не должны помнить ничего, кроме вектора и индекса.

Теперь я мог бы сам написать такой класс. Кто-нибудь (Boost? STL?) Уже сделал это для меня?

Редактировать : Ответы не относятся к моему вопросу. Я спросил, является ли эта функция стандартной библиотекой. Я воспринимаю ответы как "нет"?

Ответы [ 7 ]

22 голосов
/ 14 апреля 2009

Попробуйте использовать std :: pair , так как ни позиция вектора, ни индекс элемента не изменятся.
Или как класс:

template<class T> class VectorElementPointer
{
  vector<T>& vectorref;
  typename vector<T>::size_type index;
public:
  VectorElementPointer(vector<T>& vref, typename vector<T>::size_type index):vectorref(vref),index(index){}
  T& operator*() const {return vectorref[index];}
  T* operator->() const {return &vectorref[index];}
};

Это самое простое решение, которое приходит мне в голову, поскольку ни в STL, ни в Boost нет ничего, что могло бы сделать это проще.

9 голосов
/ 14 апреля 2009
4 голосов
/ 15 апреля 2009

Подведем итог некоторым идеям. Вот минималистическая оболочка, которая пытается имитировать итераторы, но остается действительной как противоположность векторам.

void print(const std::string& i)
{
    std::cout << "<" << i << "> ";
}
int main()
{
    typedef std::vector<std::string> Vector;

    Vector v;
    v.push_back("H");
    v.push_back("E");
    v.push_back("W");
    StrongIterator<Vector> it0(v, 0);
    StrongIterator<Vector> it3(v, v.end());

    std::for_each(it0.it(), it3.it(), print);
    std::cout << std::endl;

    v.push_back("O");
    std::for_each(it0.it(), it3.it(), print);

    std::cout << *it0;
    std::cout << it0->c_str();

    return 0;
}

И сам итератор.

template <typename TVector>
class StrongIterator
{
public:
    typedef typename TVector::iterator iterator;
    typedef typename TVector::size_type size_type;
    typedef typename TVector::value_type value_type;
    StrongIterator(TVector& vector,
                   size_type index):
        vector_(vector),
        index_(index)
    {}
    StrongIterator(TVector& vector,
                   iterator it):
        vector_(vector),
        index_(std::distance(vector.begin(), it))
    {}
    iterator it()
    {
        iterator it = vector_.begin();
        std::advance(it, index_);
        return it;
    }
    value_type& operator*()
    {
        return vector_[index_];
    }
    value_type* operator->()
    {
        return &vector_[index_];
    }
private:
    TVector& vector_;
    size_type index_;
};
2 голосов
/ 15 апреля 2009

Использование boost :: iterator_facade :

// Warning: Untested, not even compiled
template<class VectorT>
class VectorIndex : 
    public boost::iterator_facade<VectorIndex, typename VectorT::reference, boost::random_access_traversal_tag>
{
public:
    VectorIndex(VectorT& Vec, typename VectorT::size_type Index)
    : m_Vec(Vec), m_Index(Index)
    {
    }

private:
    friend class boost::iterator_core_access;

    void increment()
    {
        ++m_Index;
    }

    void decrement()
    {
        --m_Index;
    } 

    void advance(difference_type N)
    {
        m_Index += N;
    }

    difference_type distance_to(const VectorIndex& Other)
    {
        assert(&this->m_Vec == &Other.m_Vec);
        return Other.m_Index = this->m_Index; 
    }

    bool equal(const VectorIndex& Other)const
    {
        return (this->m_Vec == Other.m_Vec)
            && (this->m_Index == Other.m_Index);
    }

    VectorT::reference dereference() const 
    {
        return m_Vec[m_Index];
    }

    VectorT m_Vec;
    VectorT::size_type m_Index;
};
1 голос
/ 14 апреля 2009

К сожалению, после того, как вы измените вектор, итераторы, которые «укажут» на элемент вектора, больше не гарантируются как действительные. Единственная известная мне структура STL, которая будет поддерживать действительность итераторов даже при изменении структуры - это список <>. Если вы хотите только последовательную итерацию ваших структур, вы можете использовать std :: list <>, иначе я не знаю ни одной другой библиотеки, которая могла бы вам помочь; это не значит, что его нет.

Вот некоторая понятная документация по std :: list: http://www.cplusplus.com/reference/stl/list/

0 голосов
/ 15 апреля 2009

В зависимости от вашего шаблона использования, std :: deque может соответствовать вашим требованиям. Указатели в deque становятся недействительными только в том случае, если вы вставляете или удаляете элементы не в начале или в конце - в других словах push_front () и push_back () не делают недействительными указатели в deque, но другие изменения делают. Вы получаете в основном тот же интерфейс, что и вектор, но, конечно, основное хранилище не является смежным.

0 голосов
/ 14 апреля 2009

Если вы не напишите свою собственную версию вектора и умного указателя, то нет никакого способа, которым указатель будет действительным после перераспределения. Если бы у вас были свои собственные реализации, умный вектор мог отправлять уведомления на ваши умные указатели.

Однако я думаю, что весь сценарий - это плохой дизайн, и вам, возможно, лучше изменить свой сценарий, чтобы у вас не было таких требований.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...