2010-07-20 29 views
29

Tengo un std::vector con elementos de alguna clase ClassA. Además, quiero crear un índice usando un std::map<key,ClassA*> que mapea algunos valores clave para los punteros a los elementos contenidos en el vector.Punteros a elementos de std :: vector y std :: list

¿Hay alguna garantía de que estos punteros siguen siendo válidos (y apuntan al mismo objeto) cuando los elementos se añaden al final del vector (no insertada). Es decir, sería el siguiente código sea correcto:

std::vector<ClassA> storage; 
std::map<int, ClassA*> map; 

for (int i=0; i<10000; ++i) { 
    storage.push_back(ClassA()); 
    map.insert(std::make_pair(storage.back().getKey(), &(storage.back())); 
} 
// map contains only valid pointers to the 'correct' elements of storage 

¿Cómo es la situación, si uso std::list en lugar de std::vector?

+1

¿Cuál es el propósito del vector aquí? ¿Necesitas recordar el orden en que fueron creados? Puede usar el mapa y vecor en su lugar. Iteradores/Punteros/Las referencias a los elementos de un mapa permanecen válidos por más tiempo. Vea las garantías de su referencia de biblioteca estándar favorita. – sellibitze

Respuesta

24

Vectores - No. Debido a que la capacidad de los vectores nunca se reduce, se garantiza que las referencias, punteros e iteradores permanezcan válidos incluso cuando los elementos se eliminan o cambian, siempre que se refieran a una posición anterior a los elementos manipulados. Sin embargo, las inserciones pueden invalidar referencias, punteros e iteradores.

Listas - Sí, insertar y eliminar elementos no invalidan los punteros, las referencias y los iteradores a otros elementos

+1

Las respuestas deben revertirse, vectores -> no y listas -> sí, ya que la pregunta es "¿Hay alguna garantía de que estos indicadores sigan siendo válidos?" – Naveen

+0

@Naveen - Editado como se señaló, gracias. – DumbCoder

+0

Un 'deque' también puede ser una buena opción, si se desea el acceso aleatorio y no se reasigna al agregar elementos. – foraidt

9

Por lo que tengo entendido, no hay tal garantía. Agregar elementos al vector causará la reasignación de elementos, lo que invalidará todos los punteros en el mapa.

+0

Eso es lo que pensé. ¿Sabes acerca de 'std :: list'? Después de todo, si se implementa como lista vinculada, no habría necesidad de reasignación ... – MartinStettner

+1

Creo que * puede causar * es una redacción más apropiada. Y aún así, puedo esperar que 'realloc' se use en la implementación interna, que una vez más * puede * romper los punteros. –

1

Sólo asegúrese de que ambos punteros de almacén de una manera explícita eliminar los objetos cuando no los necesita.

std::vector<ClassA*> storage; 
std::map<int, ClassA*> map; 

for (int i=0; i<10000; ++i) { 
    ClassA* a = new ClassA() 
  storage.push_back(a) 
  map.insert(std::make_pair(a->getKey(), a)) 
} 
// map contains only valid pointers to the 'correct' elements of storage 
+3

Recomiendo encarecidamente no guardar punteros desnudos en un contenedor STL. Es una receta para fugas. – sbi

+0

Hm, eso es exactamente lo que trato de evitar :). Solo podría usar el mapa en este caso (para mi problema), solo quiero tener algún contenedor para ocuparme de la desasignación de memoria. – MartinStettner

+0

Gracias por la edición (en iPad y no se puede formatear ni poner punto y coma). – Tom

3

no estoy seguro de si está garantizado, pero en la práctica storage.reserve(needed_size) debe asegurarse de no se producen reasignaciones.

¿Pero por qué no almacena los índices?
Es fácil de convertir los índices a los iteradores mediante su inclusión en el iterador comenzar (storage.begin()+idx) y es fácil de convertir cualquier iterador en un puntero por primera eliminación de referencias y, a continuación, tomar su dirección (&*(storage.begin()+idx)).

+0

El problema es que no sé 'needed_size' de antemano (que admitir que el código es un poco simplificada ...) índices Almacenamiento serían una opción, pero también tengo que pasar punteros a varias otras partes del programa que no debería tener acceso al vector (de nuevo, el código no muestra ese aspecto) – MartinStettner

+0

@MartinStettner: Puede convertir fácilmente los índices en punteros para un vector. He expandido mi respuesta para explicarlo. – sbi

+1

Todo se encapsula en una clase que necesita pasar punteros al "exterior", otras partes del programa también pueden almacenar estos punteros, por lo que deben ser constantes. Si utilizara su enfoque, tendría que proporcionar también el iterador begin() que sería una violación de la encapsulación (el almacenamiento de vectores debería ser un detalle de implementación interna ...). – MartinStettner

6

Use std::deque! Los punteros a los elementos son estables cuando solo se usa push_back().

Nota: ¡Los iteradores de elementos pueden ser invalidados! Los punteros a los elementos no.

Editar: esta respuesta se explican los detalles de por qué: C++ deque's iterator invalidated after push_front()

+1

¿Estás seguro de esto? ¿Hay alguna parte en el estándar de C++ que cubra este reclamo? Podría ser implementado la mayor parte del tiempo de esta manera, pero necesito algún tipo de garantía ... – MartinStettner

+0

¿Por qué un iterador, que es básicamente un puntero * diseñado específicamente para ese contenedor *, se invalidarán, pero no un puntero prima, que representa nada más que una dirección de memoria (y un tipo)? – foraidt

+2

@mxp: un iterador debe ser capaz de encontrar el siguiente elemento. Esta habilidad requiere información adicional en el iterador, y esta información adicional puede ser invalidada. – Sjoerd

1

Desde uno de los comentarios a otra respuesta, parece como si todo lo que desea es centralizar la gestión de memoria (facilidad). Si ese es realmente el caso, debería considerar el uso de soluciones preempaquetadas como la biblioteca boost pointer container y mantener su propio código lo más simple posible.

En particular, echar un vistazo a ptr_map

+0

Muchas gracias por señalar esto. Desafortunadamente este proyecto es para un gran cliente que no (todavía) no quiere que incluya la biblioteca impulso en su código (aunque también podría aliviar muchos problemas :) ...) – MartinStettner

0
  1. para los vectores no.
  2. para las listas sí. cómo? iterador funciona como un puntero a un nodo particular en la lista. para que pueda asignar valores a cualquier estructura como:

    list mylist;

    par < lista :: iterator, int> temp;

    temp = make_pair (mylist.begin(), x);

+0

Esto es literalmente la misma respuesta que [DumbCoder de] (https://stackoverflow.com/a/3287828/501011), solo 7 años tarde y peor en todos los aspectos –

+0

Tuve el problema similar y estaba buscando un ejemplo simple. Como no estaba allí en ninguna de las respuestas anteriores, pensé en escribir un ejemplo yo mismo. –

Cuestiones relacionadas