2008-09-26 23 views
150

Diseñando un nuevo sistema desde cero. Usaré el STL para almacenar listas y mapas de ciertos objetos de larga duración.¿Debo almacenar objetos enteros o punteros a objetos en contenedores?

Pregunta: ¿Debo asegurarme de que mis objetos tengan constructores de copia y almacenen copias de los objetos dentro de mis contenedores STL, o que sea mejor administrar el visor de vida & y almacenar los punteros en esos objetos en mis contenedores STL?

Me doy cuenta de que esto es algo corto en detalles, pero estoy buscando la mejor respuesta "teórica" ​​si existe, ya que sé que ambas soluciones son posibles.

Dos desventajas muy obvias para jugar con los punteros: 1) Debo gestionar la asignación/desasignación de estos objetos yo mismo en un ámbito más allá de la STL. 2) No puedo crear un objeto temporal en la pila y agregarlo a mis contenedores.

¿Hay algo más que me falta?

+31

dios me encanta este sitio, esta es la pregunta EXACTA en la que estaba pensando hoy ... gracias por hacer el trabajo de preguntar por mí :-) – eviljack

+2

otra cosa interesante es que deberíamos verificar si el puntero realmente se agregó a la colección y si no es así, probablemente deberíamos llamar a delete para evitar fugas de memoria ... if ((set.insert (pointer)). second = false) {delete pointer;} – javapowered

Respuesta

61

Dado que las personas son campanadas en la eficiencia del uso de punteros.

Si está pensando en utilizar un std :: vector y si las actualizaciones son pocas y con frecuencia itera sobre su colección y es un objeto de almacenamiento de tipo no polimórfico, las "copias" serán más eficientes ya que obtendrá una mejor localidad de referencia.

Otoh, si las actualizaciones son comunes, el almacenamiento de punteros ahorrará los costos de copia/reubicación.

+12

Buen punto sobre la localidad de caché. +1 – Branan

+6

En términos de localidad de memoria caché, almacenar punteros en vector puede ser eficiente si se usa junto con un asignador personalizado para los extremos. El asignador personalizado debe ocuparse de la localidad de la memoria caché, por ejemplo, utilizando la ubicación nueva (consulte http://en.wikipedia.org/wiki/Placement_syntax#Custom_allocators). –

34

Si está almacenando objetos polimórficos, siempre debe usar una colección de punteros de clase base.

Es decir, si planea almacenar diferentes tipos derivados en su colección, debe almacenar punteros o ser comido por el slicing deamon.

+0

¡Me encantó el slicing deamon! – idichekop

3

Parece que tiene una buena comprensión de la diferencia. Si los objetos son pequeños y fáciles de copiar, almacénelos.

De lo contrario, pensaría en almacenar punteros inteligentes (no auto_ptr, un puntero inteligente de recuento inteligente) a los que usted asigna en el montón. Obviamente, si opta por punteros inteligentes, entonces no puede almacenar objetos asignados a la pila de temperatura (como ha dicho).

@Torbjörn hace un buen punto acerca de rebanar.

+1

Ah, y * nunca * * nunca * * alguna vez * crear una colección de auto_ptr –

+0

Derecha, auto_ptr no es un puntero inteligente, no cuenta. –

+0

auto_ptr tampoco tiene semántica de copia no destructiva. El acto de asignación de un auto_ptr de 'one' a' another' liberará la referencia de 'one' y cambiará' one'. –

17

por qué no obtener lo mejor de ambos mundos: hacer un contenedor de punteros inteligentes (como boost::shared_ptr o std::shared_ptr). No tiene que administrar la memoria, y no tiene que lidiar con operaciones de copia grandes.

+0

¿En qué se diferencia este enfoque de lo que Nick Haddad sugirió al usar Boost Pointer Container Library? –

+8

@ ThorstenSchöning std :: shared_ptr no agrega una dependencia en boost. –

11

En general, almacenar los objetos directamente en el contenedor STL es lo mejor ya que es más simple, más eficiente y es más fácil de usar el objeto.

Si el objeto en sí tiene sintaxis no copiable o es un tipo de base abstracta que necesitará para almacenar punteros (es más fácil de usar shared_ptr)

+3

No es lo más eficiente si sus objetos son grandes y mueve los elementos con frecuencia. – allyourcode

44

Esto realmente depende de su situación.

Si sus objetos son pequeños, y hacer una copia del objeto es liviano, entonces almacenar los datos dentro de un contenedor stl es sencillo y más fácil de manejar en mi opinión porque no tiene que preocuparse por la administración de por vida.

Si los objetos son grandes, y tener un constructor predeterminado no tiene sentido, o las copias de los objetos son caras, entonces almacenar con punteros es probablemente el camino a seguir.

Si decide usar punteros a los objetos, eche un vistazo al Boost Pointer Container Library. Esta biblioteca de impulso envuelve todos los contenedores STL para su uso con objetos dinámicamente asignados.

Cada contenedor de puntero (por ejemplo, ptr_vector) toma posesión de un objeto cuando se agrega al contenedor y gestiona el tiempo de vida de esos objetos por usted. También accede a todos los elementos en un contenedor ptr_ por referencia. Esto le permite hacer cosas como

class BigExpensive { ... } 

// create a pointer vector 
ptr_vector<BigExpensive> bigVector; 
bigVector.push_back(new BigExpensive("Lexus", 57700)); 
bigVector.push_back(new BigExpensive("House", 15000000); 

// get a reference to the first element 
MyClass& expensiveItem = bigList[0]; 
expensiveItem.sell(); 

Estas clases envuelven los contenedores STL y trabajan con todos los algoritmos de STL, lo cual es muy práctico.

También hay instalaciones para transferir la propiedad de un puntero en el contenedor a la persona que llama (a través de la función de liberación en la mayoría de los contenedores).

2

Si los objetos deben consultarse en otra parte del código, guárdelos en un vector de boost :: shared_ptr. Esto asegura que los punteros al objeto seguirán siendo válidos si cambia el tamaño del vector.

Es decir:

std::vector<boost::shared_ptr<protocol> > protocols; 
... 
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized 

Si nadie más almacena punteros a los objetos, o la lista no crecer o reducirse, simplemente almacenar objetos tan claro de edad:

std::vector<protocol> protocols; 
connection c(protocols[0]); // value-semantics, takes a copy of the protocol 
21

este momento para saltar en 3 años después del evento, pero una nota de advertencia aquí ...

En mi último gran proyecto, mi estructura de datos central era un conjunto de objetos bastante sencillos. Alrededor de un año en el proyecto, a medida que evolucionaban los requisitos, me di cuenta de que el objeto realmente necesitaba ser polimórfico. Se necesitaron unas semanas de cirugía cerebral difícil y desagradable para arreglar la estructura de datos para que sea un conjunto de punteros de clase base, y para manejar todo el daño colateral en el almacenamiento de objetos, la fundición, etc. Me llevó un par de meses convencerme de que el nuevo código funcionaba. Por cierto, esto me hizo pensar mucho sobre cuán bien diseñado está el modelo de objetos de C++.

En mi gran proyecto actual, mi estructura de datos central es un conjunto de objetos bastante sencillos. Alrededor de un año en el proyecto (que sucede hoy), me di cuenta de que el objeto realmente necesita ser polimórfico. De vuelta a la red, encontré este hilo y encontré el enlace de Nick a la biblioteca del contenedor del puntero Boost. Esto es exactamente lo que tuve que escribir la última vez para arreglar todo, así que esta vez lo intentaré.

La moraleja, para mí, de todos modos: si su especificación no está 100% moldeada en piedra, busque punteros, y posiblemente se ahorrará mucho trabajo más adelante.

+0

Las especificaciones nunca son inamovibles. No creo que eso signifique que deba usar el uso de contenedores de punteros exclusivamente, aunque los contenedores de punteros Boost parecen hacer esa opción mucho más atractiva. Soy escéptico de que necesite revisar todo el programa de una vez si decide que un contenedor de objetos se convierta en un contenedor de punteros. Este podría ser el caso en algunos diseños. En ese caso, es un diseño frágil. En ese caso, no culpe a su problema de la "debilidad" de los contenedores de objetos. – allyourcode

+0

Podría haber dejado el elemento en el vector con semántica de valores e hizo el comportamiento polimórfico dentro. –

1

Esta pregunta me ha estado molestando por un tiempo.

Me inclino por el almacenamiento de punteros, pero tengo algunos requisitos adicionales (envolturas SWIG lua) que pueden no aplicarse en su caso.

El punto más importante en este post es prueba usted mismo, utilizando sus objetos

lo hice hoy para probar la velocidad de llamar a una función miembro de una colección de 10 millones de objetos, 500 veces.

La función actualiza xey basándose en xdir y ydir (todas las variables de miembro flotante).

Usé una lista std :: para contener ambos tipos de objetos, y encontré que almacenar el objeto en la lista es ligeramente más rápido que usar un puntero. Por otro lado, el rendimiento fue muy cercano, por lo que se reduce a la forma en que se utilizarán en su aplicación.

Como referencia, con -O3 en mi hardware los punteros tardaron 41 segundos en completarse y los objetos en bruto tardaron 30 segundos en completarse.

Cuestiones relacionadas