Так как векторные элементы хранятся непрерывно, я предполагаю, что это не может иметь того же адреса после некоторого 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.: На самом деле я использую вектор класса вместо интервала, но я думаю, что проблемами является то же.
Не используйте резерв, чтобы откладывать эту ошибку с висячим указателем - как тот, кто столкнулся с той же проблемой, пожал плечами, зарезервировал 1000, затем несколько месяцев позже потратил много времени, пытаясь выяснить какую-то странную ошибку памяти (емкость вектора превысила 1000), я могу сказать вам, что это не надежное решение.
Вы хотите избегать взятия адреса элементов в векторе , если это вообще возможно, именно из-за непредсказуемой природы перераспределения. Если необходимо, используйте итераторы вместо необработанных адресов, поскольку проверенные реализации STL сообщат вам, когда они стали недействительными, вместо случайного сбоя.
Лучшее решение - изменить ваш контейнер:
В зависимости от ваших требований и случая использования, вы можете взглянуть на библиотеку контейнеров указателей Boost.
В вашем случае можно использовать boost::ptr_vector
.
Другой возможностью может быть специально созданный умный указатель, который вместо хранения адреса будет хранить адрес самого вектора вместе с индексом элемента, о котором вы заботитесь. Затем он складывает их вместе и получает адрес элемента только при разыменовании, примерно так:
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
Пара моментов: во-первых, если вы переставите элементы в векторе между моментом создания "указателя" и моментом его разыменования, он будет ссылаться уже не на исходный элемент, а на любой элемент, который окажется в указанной позиции. Во-вторых, эта функция не проверяет диапазон, поэтому (например) попытка использовать 100-й элемент в векторе, который содержит только 50 элементов, приведет к неопределенному поведению.
Как Джеймс МакНеллис и Александр Гесслер заявлено, что резерв
- хороший способ предварительного выделения памяти. Однако для полноты картины я хотел бы добавить, что для того, чтобы указатели оставались действительными, все операции вставки / удаления должны происходить из хвоста вектора, иначе смещение элемента снова сделает ваши указатели недействительными.
Вы можете увеличить емкость базового массива, используемого вектором, вызвав его функцию-член reserve
:
v.reserve(100);
Пока вы не поместите в вектор более 100 элементов, ptr
будет указывать на первый элемент.
Как это можно гарантировать?
std::vector
гарантированно является непрерывным, но реализация вольна перераспределять или освобождать память при операциях, изменяющих содержимое вектора (итераторы вектора, указатели или ссылки на элементы также становятся неопределенными).
Однако вы можете добиться желаемого результата, вызвав reserve
. IIRC, стандарт гарантирует, что никакие перераспределения не будут выполняться, пока размер вектора не превысит его зарезервированную емкость.
Вообще, я бы был осторожен с этим (можно быстро попасть в ловушку...). Лучше не полагайтесь на std::vector<>::reserve
и постоянство итератора, если только вам это действительно не нужно.
Если вам не нужно, чтобы ваши значения сохранялись непрерывно, вы можете вместо этого использовать std :: deque из std :: vector. Он не перераспределяет, но хранит элементы в нескольких блоках памяти.