2012-01-05 6 views
7

Supongamos que tengo un vector de enteros,ubicaciones de puntero de vector garantizadas?

std::vector<int> numbers; 

que se rellena con un montón de valores, entonces me dicen que haga esto (donde existe una entrada a 43)

int *oneNumber = &numbers[43]; 

Está oneNumber garantiza que siempre apunte al int en el índice 43, incluso si digamos que cambio el tamaño de los números a algo como numbers.resize (46)?

No estoy 100% seguro de qué comportamiento esperado está aquí, sé que los vectores están garantizados para ser contiguos pero no estoy seguro si esa continuidad también significa que todos los índices en el vector permanecerán en el mismo lugar a lo largo de su vida.

+0

Por esta razón yo siempre desconfío de crear un vector de tipos de valor, porque, inevitablemente, necesito un puntero a los objetos dentro del vector. Así que guardo una referencia como 'int * ref = & numbers [43]' .. luego hago un par de 'push_back's en' numbers' y WHAMMO! Error: 'ref' no es válido, porque' numbers' ha sido completamente reasignado y guardado en un espacio de memoria completamente diferente. – bobobobo

+0

@bobobobo Muy cierto. Aunque para situaciones donde se conoce el número de elementos en el momento de la creación (y no, no puedo usar 'std :: array', porque' 'emplace' -construye los valores mediante bucle),' reserve' garantiza todos los punteros/iteradores en el vector seguirá siendo válido, siempre que no se redimensione más allá de su capacidad actual (lo que podría forzar una reasignación y, por lo tanto, mover las direcciones de los valores contenidos). Esto me ha salvado un par de veces, pero cada vez que paso un tiempo, me olvido de "reservar" y me rasco la cabeza por un tiempo hasta que lo recuerdo. : D –

Respuesta

7

Está oneNumber garantiza que siempre se apunta a la int en el índice 43

Sí, esto está garantizado por la norma.

aunque digamos que cambio el tamaño de los números a algo así como numbers.resize (46)?

No. Una vez que cambia el tamaño, agrega o quita algo al vector, se invalidan todas las direcciones e iteradores. Esto se debe a que el vector puede necesitar ser reasignado con nuevas ubicaciones de memoria.

+0

¿Qué tal si usas una 'std :: list'? ¿Evitará eso el problema de la dirección no válida si solo usa las llamadas 'list.push_back()' para agregar elementos? (y no elimina el elemento al que 'oneNumber' apunta? – bobobobo

+0

@bobobobo Esa es una buena pregunta. No estoy seguro de lo que dice el estándar al respecto, pero no veo ninguna razón para agregar/eliminar un' std: : list' anularía cualquier puntero. (excepto el elemento que se elimina) [Lo estamos discutiendo en el salón en este momento si quieres unirte.] (http://chat.stackoverflow.com/transcript/message/5559322 # 5559322) – Mysticial

+0

Encontré [otra solución alternativa] (http://stackoverflow.com/a/12771369/111307) que estoy usando actualmente. – bobobobo

2

No: el vector se puede reasignar cuando crece. Por lo general, una vez que el vector se duplica en tamaño.

Desde el C++ 11 estándar

1 Remarks: Causes reallocation if the new size is greater than the old capacity. If no 
reallocation happens, all the iterators and references before the insertion point 
remain valid. If an exception is thrown other than by the copy constructor, move 
constructor, assignment operator, or move assignment operator of T or by any 
InputIterator operation there are no effects. If an exception is thrown by the move 
constructor of a non-CopyInsertable T, the effects are unspecified. 
4

Su paranoia es correcta. Cambiar el tamaño de un std::vector puede hacer que cambie la ubicación de su memoria. Eso significa que su oneNumber apunta ahora a una ubicación de memoria antigua que se ha liberado, por lo que acceder a ella es un comportamiento indefinido.

2

Cuando usa la función resize() o reserve() de un vector para aumentar la capacidad del vector, puede necesitar reasignar memoria para la matriz de respaldo. Si se reasigna, la nueva memoria no se ubicará en la misma dirección, por lo que la dirección almacenada en oneNumber ya no apuntará al lugar correcto.

De nuevo, esto depende de la cantidad de elementos que el vector está siendo utilizado actualmente para almacenar y el tamaño solicitado. Dependiendo de las especificaciones, el vector puede ser capaz de redimensionar sin reasignar, pero definitivamente no debe asumir que este será el caso.

4

punteros, referencias, y los iteradores a std::vector elementos están garantizados para quedarse todo el tiempo que sólo se anexa a la std::vectory el tamaño de la std::vector no crece más allá de su capacity() en el momento del puntero, de referencia, o iterador fue obtenido. Una vez que se redimensiona más allá del capacity(), todos los punteros, referencias e iteradores a este std::vector quedan invalidados. Tenga en cuenta que las cosas también se invalidan cuando se inserta en otro lugar que no sea el final de std::vector.

Si desea que sus objetos permanezcan en su lugar y solo inserte elementos nuevos al final o al principio, puede usar std::deque.Los punteros y las referencias a elementos en std::deque solo se invalidan cuando se inserta en medio de std::deque o cuando se quita del medio o cuando se quita el objeto al que se hace referencia. Tenga en cuenta que los iteradores de los elementos en el std::deque se invalidan cada vez que inserta un elemento en el std::deque o elimina cualquier elemento del mismo.

2

Una vez que cambió la capacidad del vector, los datos se copiaron en otro bloque de memoria y se borraron los datos de origen.

3

Como han dicho todos los demás, cuando llama al .resize() en un vector, sus punteros se invalidan porque la (antigua matriz) puede ser completamente desasignada, y una totalmente nueva puede ser reasignada y sus datos copiados en ella.

Una solución para esto es no almacenan punteros en un vector de STL. En su lugar, almacene los índices enteros.

Así que en tu ejemplo,

std::vector<int> numbers; 
int *oneNumber = &numbers[43]; // no. pointers invalidated after .resize or possibly .push_back. 
int oneNumberIndex = 43 ;  // yes. indices remain valid through .resize/.push_back 
Cuestiones relacionadas