2012-04-29 14 views
6

Sé que es un debate muy antiguo que ya se ha debatido muchas veces en todo el mundo. Pero actualmente tengo problemas para decidir qué método debo usar en lugar de otro entre arreglos estáticos y dinámicos en un caso particular. En realidad, no hubiera usado C++ 11, habría utilizado matrices estáticas. Pero ahora estoy confundido ya que podría haber beneficios equivalentes con ambos.Matrices estáticas VS. matrices dinámicas en C++ 11

Primera solución:

template<size_t N> 
class Foo 
{ 
    private: 
     int array[N]; 

    public: 
     // Some functions 
} 

Segunda solución:

template<size_t N> 
class Foo 
{ 
    private: 
     int* array; 

    public: 
     // Some functions 
} 

No puedo pasar a elegir ya que los dos tienen sus propias ventajas:

  • matrices estáticas son más rápidos, y no nos importa la gestión de la memoria en absoluto.
  • Las matrices dinámicas no pesan nada mientras no se haya asignado la memoria. Después de eso, son menos útiles de usar que las matrices estáticas. Pero desde C++ 11, podemos obtener grandes beneficios de la semántica de movimientos, que no podemos usar con matrices estáticas.

No creo que haya una buena solución, pero me gustaría obtener algunos consejos o solo para saber qué piensa de todo eso.

+2

Sus dos soluciones deben ser: su primera, o una plantilla de vector y ninguna size_t.Debe evitar el uso de punteros sin formato donde se pueda usar un contenedor STL sin ningún problema. – mfontanini

+13

@fontanini: si tuviera que modificar para C++ 11, entonces las dos soluciones deberían ser 'std :: array ' o 'std :: vector '. –

+1

@ DavidRodríguez-dribeas sí señor! – mfontanini

Respuesta

5

En realidad no estoy de acuerdo con el "depende". Nunca use la opción 2. Si desea usar una constante de tiempo de traducción, siempre use la opción 1 o std :: array. La única ventaja que enumera, que las matrices dinámicas no pesan nada hasta que se las asigna, es en realidad una desventaja enorme y horrible, y una que debe señalarse con gran énfasis.

Nunca tenga objetos que tengan más de una fase de construcción. Nunca jamás. Esa debe ser una regla comprometida con la memoria a través de un gran tatuaje. Simplemente nunca lo hagas.

Cuando tienes objetos zombies que aún no están del todo vivos, aunque no del todo muertos, la complejidad en la gestión de su vida crece exponencialmente. Debes verificar en cada método si está completamente vivo o si solo pretende estar vivo. La seguridad de excepciones requiere casos especiales en su destructor. En lugar de una simple construcción y destrucción automática, ahora ha agregado requisitos que deben verificarse en N lugares diferentes (# methods + dtor). Y al compilador no le importa si lo comprueba. Y otros ingenieros no tendrán este requisito de transmisión, por lo que pueden ajustar su código de forma insegura, utilizando variables sin verificar. Y ahora todos estos métodos tienen múltiples comportamientos dependiendo del estado del objeto, por lo que cada usuario del objeto necesita saber qué esperar. Zombies arruinará su (codificación) vida.

En su lugar, si tiene dos duraciones naturales diferentes en su programa, use dos objetos diferentes. Pero eso significa que tiene dos estados diferentes en su programa, por lo que debe tener una máquina de estado, con un estado que tiene un solo objeto y otro estado con ambos, separados por un evento asincrónico. Si no hay un evento asíncrono entre los dos puntos, si todos encajan en un ámbito de función, entonces la separación es artificial y usted debería estar haciendo una construcción de fase única.

El único caso en que un tamaño de tiempo de traducción se debe traducir a una asignación dinámica es cuando el tamaño es demasiado grande para la pila. A continuación, se optimiza la memoria y siempre se debe evaluar utilizando herramientas de memoria y creación de perfiles para ver qué es lo mejor. La opción 2 nunca será la mejor (usa un puntero desnudo, así que de nuevo perdemos RAII y cualquier limpieza y administración automática, agregando invariantes y haciendo que el código sea más complejo y fácilmente divisible por otros). Vector (como lo sugiere la máscara de bits) sería la primera idea apropiada, aunque es posible que no le gusten los costos de asignación del montón a tiempo. Otras opciones pueden ser espacio estático en la imagen de su aplicación. Pero, una vez más, esto solo debe considerarse una vez que haya determinado que tiene una restricción de memoria y qué hacer desde allí debe estar determinado por las necesidades medibles reales.

+0

Sí, su comentario tiene sentido. Cuando volví a leer mi código, me di cuenta de que en realidad nunca había tenido objetos zombis ya que la memoria se asignó en la construcción. Entonces no te preocupes por eso. Correcto, las únicas ventajas fueron la ganancia de las operaciones de movimiento al final (ya proporcionado por std :: vector si se usa). El único inconveniente de usar un vector allí sería una sobrecarga en comparación con std :: array. Pero no sé si std :: array tiene semántica de movimiento ... – Morwenn

+0

En la pila, normalmente no necesita movimientos, necesita la optimización del valor de retorno, que aunque es específica del compilador, es bastante universal. Otros casos en los que se pueden usar movimientos casi siempre se pueden realizar en el lugar (sin copia), o cuando se necesita una copia, en realidad se necesita (para poner la memoria de un tipo en un lugar diferente), por lo que los movimientos no funcionan. – ex0du5

4

No utilice ninguno de los dos. Es mejor utilizar std::vector en casi cualquier caso. En los otros casos, eso depende en gran medida del motivo por el cual std::vector sería insuficiente y, por lo tanto, ¡no se puede responder en general!

+0

Ya conozco esta regla de oro. Sería para un trabajo profesional, lo usaría. Sin embargo, me gusta experimentar y ganar experiencia al intentarlo. Además, realmente no necesitaba nada más que acceso aleatorio en el presente caso. Es por eso que consideré intentar esto. Y, sí, como std :: vector depende de la implementación, puede haber una gran sobrecarga si uso muchas instancias de esta clase ya que el tamaño de la matriz en realidad no debe cambiarse después. – Morwenn

4

Actualmente estoy teniendo un problema para decidir cuál debo usar más que otro en un caso particular.

Deberá considerar sus opciones caso por caso para determinar la solución óptima para el contexto dado, es decir, no se puede hacer una generalización. Si un contenedor fuera ideal para cada escenario, el otro sería obsoleto.

Como se mencionó anteriormente, considere usar implementaciones std antes de escribir las suyas propias.

Más detalles:

longitud fija

  • Sé cuidadoso de la cantidad de la pila se consume.
  • Puede consumir más memoria, si la trata como un contenedor de tamaño dinámico.
  • Copias rápidas.

longitud variable

  • reasignación y cambio de tamaño puede ser costoso.
  • Puede consumir más memoria de la necesaria.
  • Movimientos rápidos.

La mejor opción también requiere a entender la complejidad de la creación, copia, asignar, etc., de los tipos de elementos.

Y si utiliza implementaciones std, recuerde que las implementaciones pueden variar.

Finalmente, puede crear un contenedor para estos tipos que abstraiga los detalles de implementación y seleccione dinámicamente un miembro de datos apropiado según el tamaño y el contexto: abstracción de los detalles detrás de una interfaz general. Esto también es útil a veces para desactivar funciones o para hacer algunas operaciones (por ejemplo, copias costosas) más obvias.

En resumen, necesita saber mucho sobre los tipos y el uso, y medir varios aspectos de su programa para determinar el tipo de contenedor óptimo para un escenario específico.

+1

En el presente caso, dado que necesitaba especializaciones de plantilla para N <4, pensé que podría usar matrices estáticas para aquellas y dinámicas cuando N es más grande. La memoria se asigna una vez y la matriz nunca se redimensiona, no es una gran sobrecarga. Como me gustan sus ideas en la última parte de su respuesta. ¡Gracias! – Morwenn

+0

@Morwenn de nada :) – justin

+1

¿Cómo difieren los tiempos de copiado? – ex0du5