2012-09-17 15 views
7

Estoy tomando una clase de estructuras de datos, y en todos los ejemplos del profesor, siempre hace que su mapa tenga un valor de puntero a una estructura o contenedor como opuesto a mantener la estructura o contenedor en sí.Mapa de Punteros versus Mapa de Estructuras/Contenedores (C++)

Es que acaba de hacerlo como un hábito o hay una buena razón para ello, tales como aumentos de velocidad?

  • Soy consciente de que puede usar punteros a los datos para evitar tener copias redundantes de datos y, al mismo tiempo, mantener las direcciones a esos datos en múltiples contenedores/estructuras al mismo tiempo.
  • En estos ejemplos, ese es el caso. Los datos están solo en ese mapa.
+2

¿Por qué no le preguntas al profesor? No es una gran pregunta. – John3136

+0

Puedo decirte en la práctica que para mí es más de 15 años de desarrollo profesional en C++ y más de 1 millón de líneas de código escritas en el trabajo. Raramente pongo una estructura o clase dentro de un contenedor. Más del 99% de las veces utilizo un puntero de algún tipo (recientemente, lo más probable es que sea un puntero inteligente). – drescherjm

+0

Aunque ya he respondido, me pregunto si la pregunta se debe considerar un duplicado de http://stackoverflow.com/questions/141337/c-stl-should-i-store-entire-objects-or-pointers-to -objetos. – jogojapan

Respuesta

9

Tal como lo veo, hay una serie de factores que intervienen en la decisión de usar punteros frente a objetos:

1. ¿o no te necesito polimorfismo?

Si desea mantener un contenedor de objetos de clase base, pero luego almacenar objetos de varias clases derivadas en él, debe usar punteros, ya que de lo contrario las llamadas a funciones virtuales no se resolverían correctamente.

2. El tamaño de los objetos se almacenan y su idoneidad para operaciones de copia

Una de las razones clave por las que los punteros pueden ser preferible a objetos es que diversas operaciones realizadas en el contenedor requieren hacer copias de la objetos almacenados en ella. Este es el caso para muchas operaciones de almacenamiento (por ejemplo, std::vector<>::push_back() o std::map<>::insert()), algunas operaciones de recuperación (por ejemplo, std::vector<>::operator[] y luego almacenar el objeto en una variable local) y algunas de las operaciones llevadas a cabo por el contenedor "internamente", p. reasignación de un vector cuando crece más allá de su capacidad, o reajuste de std::unordered_map<>. Tenga en cuenta que las operaciones de copia pueden ser menos importantes dependiendo de cómo elija el contenedor y cómo hacer uso de él (p.usando std::vector<>::reserve() para asignar suficiente espacio, usando std::vector<>::emplace_back() para el almacenamiento, y nunca hacer una copia local de un elemento recuperado puede significar que nunca se realiza una copia).

Sin embargo, si espera una gran cantidad de copias (o si el código existente revela que se han hecho muchas copias), usar punteros en lugar de objetos obviamente puede ayudar ya que los punteros son pequeños y están bien alineados en la memoria. Por otra parte, esto no tendrá mucho sentido si los objetos que almacena son en realidad más pequeños que los punteros.

3. Otras operaciones que se realizan en el recipiente y su contenido

Incluso si los objetos que se están tratando son más grandes que los punteros y que esperan una cantidad significativa de las operaciones de copia, el uso de punteros no es necesariamente preferible . Considere una situación en la que almacene una gran cantidad de objetos de tamaño mediano (digamos, 16 bytes cada uno) y con frecuencia necesitará iterar sobre todo el contenedor y realizar algún tipo de cálculo estadístico. Cuando almacena estos objetos directamente en un vector, obtiene una gran eficiencia de caché durante la iteración: a medida que recupera un objeto, se recuperará una línea de caché completa de la memoria, lo que hace que la recuperación de los siguientes objetos sea mucho más rápida. Esto generalmente no es el caso cuando se usan punteros; por el contrario, después de recuperar un elemento, se debe desreferenciar el puntero, lo que causa otra operación de movimiento desde una región de memoria que posiblemente no esté en caché.

Así que, claramente, todo depende del tipo y tamaño de los objetos que almacena, y del tipo y la frecuencia de las operaciones que lleva a cabo. Si los objetos con los que trata son varios tipos de ventanas, botones y menús de una aplicación GUI, lo más probable es que quiera utilizar punteros y aprovechar el polimorfismo. Si, por otro lado, se trata de enormes estructuras de elementos compactos, todos idénticos en tamaño y forma, y ​​las operaciones que realiza implican iteraciones frecuentes o copias masivas, el almacenamiento de objetos directamente es perferible. También puede haber situaciones en las que sea difícil tomar una decisión sin probar ambas y decidir en función de los resultados de los puntos de referencia de memoria y tiempo.


Como nota final, si al final el uso de punteros, tenga en cuenta si el contenedor está construyendo es el último propietario de los objetos va a asignar en el montón, o simplemente mantiene punteros temporales. Si el contenedor es el propietario de esos objetos, será aconsejable utilizar punteros inteligentes en lugar de crudos.

1

Una posibilidad es que el tipo de contenido no se pueda copiar.

1

La ventaja de almacenar las instancias de objeto directamente en el contenedor es que evita un nivel de indirección & que guarda en el espacio que utiliza el puntero. Usted puede ganar en términos de tiempo & eficiencia de espacio almacenando instancias de objeto directamente en lugar de almacenar punteros. Si comprende un poco sobre cómo funciona la memoria caché del procesador, no es difícil ver cómo el almacenamiento de instancias de objetos "en línea" en el contenedor puede tener un beneficio de rendimiento real.

Sin hacer ninguna suposición sobre el tipo de contenido o el patrón de uso de contenedores, el recipiente predeterminado debería ser std::vector<T> (y no std::vector<T*>). A partir de esa opción predeterminada, usará algo que no sea un vector si puede ver cómo se beneficiará el patrón de uso del perfil de rendimiento de ese otro tipo de estructura. Del mismo modo, tendrá los punteros de la tienda de contenedores en los objetos si la indirección del puntero es requerida o parece valer la pena en términos de rendimiento. Se requiere Indirection si el tipo contenido no es de construcción de copia, y también se requiere si el contenedor no "posee" sus objetos.