Делает станд.:: вектор изменяет свой адрес? Как избежать

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

Я работаю над кодом, где мне нужна ссылка на элемент в векторе, как:

int main(){
    vector<int> v;
    v.push_back(1);
    int *ptr = &v[0];
    for(int i=2; i<100; i++)
        v.push_back(i);
    cout << *ptr << endl; //?
    return 0;
}

Но это не обязательно верно это ptr содержит ссылку на v[0], право? Как был бы хороший путь для гарантии этого?

Моя первая идея состояла бы в том, чтобы использовать вектор указателей и динамического выделения. Я задаюсь вопросом, существует ли более легкий способ сделать это?

PS.: На самом деле я использую вектор класса вместо интервала, но я думаю, что проблемами является то же.

9
задан James McNellis 19 September 2010 в 15:55
поделиться

7 ответов

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

Вы хотите избегать взятия адреса элементов в векторе , если это вообще возможно, именно из-за непредсказуемой природы перераспределения. Если необходимо, используйте итераторы вместо необработанных адресов, поскольку проверенные реализации STL сообщат вам, когда они стали недействительными, вместо случайного сбоя.

Лучшее решение - изменить ваш контейнер:

  • Вы можете использовать std :: list - он не делает недействительными существующие итераторы при добавлении элементов, и только итератор для стертого элемента становится недействительным при стирании
  • Если вы вы используете C ++ 0x, std :: vector > - интересное решение
  • В качестве альтернативы, использование указателей и new / delete не так уж плохо - просто не забывайте для удаления указателей перед их стиранием . Это несложно сделать так, но вы должны быть очень осторожны, чтобы не вызвать утечку памяти, забыв об удалении. (Марк Рэнсом также указывает: это небезопасно для исключений, все содержимое вектора утекает, если исключение приводит к уничтожению вектора.)
  • Обратите внимание, что ptr_vector boost не может безопасно использоваться с некоторыми алгоритмами STL, что может быть проблемой для вас.
12
ответ дан 4 December 2019 в 06:22
поделиться

В зависимости от ваших требований и случая использования, вы можете взглянуть на библиотеку контейнеров указателей Boost.

В вашем случае можно использовать boost::ptr_vector.

1
ответ дан 4 December 2019 в 06:22
поделиться

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

template <class T>
class vec_ptr { 
    std::vector<T> &v;
    size_t index;
public:
    vec_ptr(std::vector<T> &v, size_t index) : v(v), index(index) {}

    T &operator*() { return v[index]; }
};

Тогда ваш int *ptr=&v[0]; будет заменен чем-то вроде: vec_ptr ptr(v,0);

Пара моментов: во-первых, если вы переставите элементы в векторе между моментом создания "указателя" и моментом его разыменования, он будет ссылаться уже не на исходный элемент, а на любой элемент, который окажется в указанной позиции. Во-вторых, эта функция не проверяет диапазон, поэтому (например) попытка использовать 100 элемент в векторе, который содержит только 50 элементов, приведет к неопределенному поведению.

5
ответ дан 4 December 2019 в 06:22
поделиться

Как Джеймс МакНеллис и Александр Гесслер заявлено, что резерв - хороший способ предварительного выделения памяти. Однако для полноты картины я хотел бы добавить, что для того, чтобы указатели оставались действительными, все операции вставки / удаления должны происходить из хвоста вектора, иначе смещение элемента снова сделает ваши указатели недействительными.

2
ответ дан 4 December 2019 в 06:22
поделиться

Вы можете увеличить емкость базового массива, используемого вектором, вызвав его функцию-член reserve:

v.reserve(100);

Пока вы не поместите в вектор более 100 элементов, ptr будет указывать на первый элемент.

9
ответ дан 4 December 2019 в 06:22
поделиться

Как это можно гарантировать?

std::vector гарантированно является непрерывным, но реализация вольна перераспределять или освобождать память при операциях, изменяющих содержимое вектора (итераторы вектора, указатели или ссылки на элементы также становятся неопределенными).

Однако вы можете добиться желаемого результата, вызвав reserve. IIRC, стандарт гарантирует, что никакие перераспределения не будут выполняться, пока размер вектора не превысит его зарезервированную емкость.

Вообще, я бы был осторожен с этим (можно быстро попасть в ловушку...). Лучше не полагайтесь на std::vector<>::reserve и постоянство итератора, если только вам это действительно не нужно.

6
ответ дан 4 December 2019 в 06:22
поделиться

Если вам не нужно, чтобы ваши значения сохранялись непрерывно, вы можете вместо этого использовать std :: deque из std :: vector. Он не перераспределяет, но хранит элементы в нескольких блоках памяти.

3
ответ дан 4 December 2019 в 06:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: