¿Deben usarse matrices en C ++?

95

Dado que std::listy std::vectorexisten, ¿hay alguna razón para usar matrices C tradicionales en C ++, o deberían evitarse, al igual que malloc?

Andreas
fuente
18
@Als: Esa pregunta se refiere a la diferencia entre dos contenedores específicos, mientras que esta pregunta se refiere a la diferencia entre matrices sin procesar y contenedores estándar en general.
Jon Purdy

Respuestas:

109

En C ++ 11, donde std::arrayestá disponible, la respuesta es "sí, se deben evitar las matrices". Antes de C ++ 11, es posible que deba utilizar matrices C para asignar matrices en el almacenamiento automático (es decir, en la pila).

dasblinkenlight
fuente
3
Sin embargo, muchos compiladores aún carecen de compatibilidad con C ++ 11. Tendrá que decidir cuándo es mejor usar uno en lugar del otro dada la falta de std :: array
Nowayz
4
std :: array es una plantilla, que afecta a los proyectos grandes en términos de tiempo de construcción y posiblemente tamaño del código, ya que para cada combinación de T, N la plantilla se instancia de nuevo.
zvrba
std :: vector garantiza la alineación de datos por estándar, por lo que se puede usar en casi todas partes. Con C ++ 11 realmente no hay razón para usar matrices C.
Nils
16
Las matrices @Nils también garantizan la alineación. Además, la asignación automática de almacenamiento ("la pila") es mucho más rápida que la asignación dinámica de almacenamiento. Si que tengo exactamente 3 elementos [por ejemplo, coordenadas de un triángulo], no hay razón para usar vector.
zvrba
9
@zvrba - Verifique el ensamblado generado cuando use std :: array vs arrays C. Ninguna diferencia en absoluto.
Nemanja Trifunovic
85

Definitivamente, aunque std::arrayen C ++ 11, prácticamente solo para datos estáticos. Las matrices de estilo C tienen tres ventajas importantes sobre std::vector:

  • No requieren asignación dinámica. Por esta razón, se prefieren las matrices de estilo C donde es probable que tenga muchas matrices muy pequeñas. Di algo como un punto de n dimensiones:

    template <typename T, int dims>
    class Point
    {
        T myData[dims];
    // ...
    };

    Por lo general, uno podría imaginar que dimsserá muy pequeño (2 o 3), Tun tipo integrado ( double) y que podría terminar std::vector<Point>con millones de elementos. Definitivamente no desea millones de asignaciones dinámicas de 3 dobles.

  • La inicialización estática de soporte. Este es solo un problema para los datos estáticos, donde algo como:

    struct Data { int i; char const* s; };
    Data const ourData[] =
    {
        { 1, "one" },
        { 2, "two" },
        //  ...
    };

    Esto es a menudo preferible a usar un vector (y std::string), ya que evita todos los problemas de orden de inicialización; los datos se cargan previamente, antes de que se pueda ejecutar cualquier código real.

  • Finalmente, relacionado con lo anterior, el compilador puede calcular el tamaño real de la matriz a partir de los inicializadores. No tienes que contarlos.

Si tiene acceso a C ++ 11, std::arrayresuelve los dos primeros problemas y definitivamente debe usarse con preferencia a las matrices de estilo C en el primer caso. Sin embargo, no aborda el tercero, y tener la dimensión del compilador de la matriz de acuerdo con el número de inicializadores sigue siendo una razón válida para preferir las matrices de estilo C.

James Kanze
fuente
11
La inicialización de la matriz de estilo C también elimina la necesidad de repetirse. int i[] = { 1, 2, 3 };sigue trabajando con int i[] = { 1, 2, 3, 4 };. array<int, 3>debe cambiarse manualmente a array<int, 4>.
10
@JoeWreschnig Un cambio que puedes olvidar fácilmente. Si agrega un elemento, el compilador debería quejarse, pero si elimina uno, terminará con un elemento inicializado 0 adicional al final. Todavía utilizo matrices de estilo C ampliamente para este tipo de datos estáticos.
James Kanze
3
La primera oración no tiene sentido.
Konrad Rudolph
4
El tercer punto se puede resolver con bastante elegancia usando una make_arrayfunción , similar a make_pairetc. Hat-tip to @R. Martinho Fernandes .
Konrad Rudolph
@KonradRudolph: Claro que sí. Andreas pregunta "¿Deberían usarse matrices en C ++?", A lo que James responde "Definitivamente, aunque std::arrayen C ++ 11, [deberían usarse] prácticamente solo para datos estáticos".
Jon Purdy
15

Nunca diga "nunca", pero estoy de acuerdo en que su función se ve muy disminuida por las estructuras de datos reales de STL.

También diría que la encapsulación dentro de los objetos debería minimizar el impacto de elecciones como esta. Si la matriz es un miembro de datos privados, puede intercambiarla sin afectar a los clientes de su clase.

duffymo
fuente
11

He trabajado en sistemas críticos para la seguridad en los que no puede utilizar la asignación de memoria dinámica. La memoria tiene que estar siempre en la pila. Por lo tanto, en este caso, usaría matrices ya que el tamaño se fija en el momento de la compilación.

Ed Heal
fuente
8
Antes de C ++ 11 habría estado de acuerdo, pero std::array<T>asigna en las pilas y básicamente no tiene gastos generales en una matriz sin formato.
111111
5
@ 111111 - De acuerdo. Pero sé de algunas personas en esa industria que aún no se han trasladado a C ++ 11
Ed Heal
Sé que es por eso que no te rechacé, pero creo que boost tenía una versión y es fácil lanzar la tuya también.
111111
6
pero en los sistemas críticos para la seguridad, no usa nuevas características del compilador (menos probadas) y no usa boost.
James Kanze
3
Muchos sistemas críticos para la seguridad se basan en compiladores ANTIGUOS que ni siquiera tienen las funciones de compilador más nuevas porque cambiar las cadenas de herramientas es un proceso lento y costoso que requiere toneladas de papeleo, pruebas y certificaciones.
Brian McFarland
6

arrayin c++le ofrece una alternativa rápida de tamaño fijo de tamaño dinámico std::vectory std::list. std :: array es una de las adiciones en c++11. Proporciona el beneficio de los contenedores estándar al mismo tiempo que proporciona la semántica de tipo agregado de las matrices de estilo C.

Entonces c++11, ciertamente usaría std::array, donde sea necesario, sobre vector. Pero evitaría la matriz de estilo C en C++03.

Vikas
fuente
4

Más generalmente, no , no puedo pensar en una razón para utilizar matrices primas sobre, por ejemplo, vectors. Si el código es nuevo .

Es posible que deba recurrir al uso de matrices si sus bibliotecas necesitan ser compatibles con el código que espera matrices y punteros sin formato.

Luchian Grigore
fuente
1
... pero desde C ++ 03 un vector "realmente tiene" una matriz, a la que puede acceder mediante puntero para leer o escribir. Eso cubre la mayoría de los casos de código que espera punteros a matrices. Es solo realmente cuando ese código asigna o libera la matriz que no puede usar un vector.
Steve Jessop
@SteveJessop ¿puedes acceder a la matriz interna?
Luchian Grigore
1
@LuchianGrigore: vector.data()en C ++ 11 o &vector.front()anteriormente.
Mike Seymour
@Luchian: siempre que el vector no esté vacío, puede tomar un puntero a un elemento (y si está vacío, puede pasar un puntero nulo y una longitud de 0 a cualquier función escrita con sensatez que acepte el caso de borde de un búfer de tamaño cero). Prácticamente, el único propósito de la garantía de contigüidad de vector agregada en C ++ 03 era permitir que los vectores se usaran como búferes por código orientado a punteros.
Steve Jessop
1
@SteveJessop Y el hecho de que mucha gente pensó que estaba garantizado de todos modos, y se consideró preferible no decepcionarlos.
James Kanze
4

Sé que mucha gente está señalando std :: array para asignar matrices en la pila y std :: vector para el montón. Pero ninguno parece apoyar la alineación no nativa. Si está haciendo cualquier tipo de código numérico en el que desea usar instrucciones SSE o VPX (por lo tanto, requiere una alineación de 128 o 256 bytes respectivamente), las matrices C aún parecen ser su mejor opción.

gct
fuente
3

Yo diría que las matrices siguen siendo útiles, si está almacenando una pequeña cantidad de datos estáticos, ¿por qué no?

james82345
fuente
2

La única ventaja de una matriz (por supuesto, envuelta en algo que administrará automáticamente su desasignación cuando sea necesario) sobre la std::vectorque puedo pensar es que vectorno puede pasar la propiedad de sus datos, a menos que su compilador admita C ++ 11 y mueva constructores.

Tadeusz Kopec
fuente
6
"el vector no puede pasar la propiedad de sus datos" - sí, puede, en C ++ 03, usando swap.
Steve Jessop
2

Los arreglos de estilo C son una estructura de datos fundamental, por lo que habrá casos en los que sea mejor usarlos. Sin embargo, para el caso general, utilice las estructuras de datos más avanzadas que redondean las esquinas de los datos subyacentes. C ++ te permite hacer cosas muy interesantes y útiles con la memoria, muchas de las cuales funcionan con matrices simples.

James Wynn
fuente
3
¿Cómo son las matrices de estilo C más fundamentales que las std::arrays? En muchos casos, ambos se compilarán en el mismo ensamblado.
izquierda rotonda alrededor
1
Más fundamental porque es más básico. Ya sabe lo que va a hacer una matriz, std :: matriz puede tener peculiaridades de implementación ya que se basa en la biblioteca estándar.
James Wynn
1
@JamesWynn En realidad, no. std::arraytiene semántica definida con precisión construida sobre matrices estáticas.
Konrad Rudolph
1

Debe usar contenedores STL internamente, pero no debe pasar punteros a dichos contenedores entre diferentes módulos, o terminará en el infierno de dependencias. Ejemplo:

std::string foo;
//  fill foo with stuff
myExternalOutputProc(foo.c_str());

es una muy buena solución pero no

std::string foo;
//  fill foo with stuff
myExternalOutputProc(&foo);

La razón es que std :: string se puede implementar de muchas formas diferentes, pero una cadena de estilo c es siempre una cadena de estilo c.

user877329
fuente
Creo que lo que está tratando de decir es: No vincule diferentes códigos de objeto juntos si se usaron diferentes compiladores / implementaciones de la biblioteca estándar para crearlos. Eso es ciertamente cierto. ¿Cómo se relaciona esto con la pregunta original?
jogojapan
Es solo un consejo sobre cuándo usar matrices o contenedores STL. Construya datos usando un contenedor, páselos como una matriz. Para otros datos que cadenas, tendría algo como myExternalOutputProc (foo.rawPointerGet (), foo.count ());
user877329
Pero estos problemas solo surgen cuando se combinan diferentes implementaciones de la biblioteca estándar en el mismo proyecto. Eso es una locura. En cualquier código normal, está perfectamente bien pasar, digamos, un vector, por referencia (o, en C ++ 11, moverlo) a una función.
jogojapan
1
Resulta que