¿Cómo declaro una matriz 2D en C++ usando nuevo?
¿Cómo declaro una matriz 2D usando nuevo?
Por ejemplo, para una matriz "normal", haría:
int* ary = new int[Size]
pero
int** ary = new int[sizeY][sizeX]
a) no funciona/compila y b) no logra lo que:
int ary[sizeY][sizeX]
hace.
Si la longitud de su fila es una constante de tiempo de compilación, C++ 11 permite
auto arr2d = new int [nrows][CONSTANT];
Vea esta respuesta . Los compiladores como gcc que permiten matrices de longitud variable como una extensión de C++ pueden usar new
como se muestra aquí para obtener una funcionalidad de dimensión de matriz totalmente variable en tiempo de ejecución como lo permite C99, pero ISO C++ portátil está limitado a que solo la primera dimensión sea variable.
Otra opción eficiente es realizar la indexación 2D manualmente en una matriz 1D grande, como muestra otra respuesta , lo que permite las mismas optimizaciones del compilador que una matriz 2D real (por ejemplo, probar o verificar que las matrices no se superponen ni se superponen).
De lo contrario, puede utilizar una matriz de punteros a matrices para permitir una sintaxis 2D como matrices 2D contiguas, aunque no sea una asignación única grande y eficiente. Puedes inicializarlo usando un bucle, como este:
int** a = new int*[rowCount];
for(int i = 0; i < rowCount; ++i)
a[i] = new int[colCount];
Lo anterior, para colCount= 5
y rowCount = 4
, produciría lo siguiente:
No olvides hacer delete
cada fila por separado con un bucle, antes de eliminar la matriz de punteros. Ejemplo en otra respuesta .
int** ary = new int[sizeY][sizeX]
debiera ser:
int **ary = new int*[sizeY];
for(int i = 0; i < sizeY; ++i) {
ary[i] = new int[sizeX];
}
y luego limpiar sería:
for(int i = 0; i < sizeY; ++i) {
delete [] ary[i];
}
delete [] ary;
EDITAR: como señaló Dietrich Epp en los comentarios, esta no es exactamente una solución liviana. Un enfoque alternativo sería utilizar un gran bloque de memoria:
int *ary = new int[sizeX*sizeY];
// ary[i][j] is then rewritten as
ary[i*sizeY+j]
Aunque esta popular respuesta le brindará la sintaxis de indexación deseada, es doblemente ineficiente: grande y lenta tanto en espacio como en tiempo. Hay una mejor manera.
Por qué esa respuesta es grande y lenta
La solución propuesta es crear una matriz dinámica de punteros y luego inicializar cada puntero en su propia matriz dinámica independiente. La ventaja de este enfoque es que le brinda la sintaxis de indexación a la que está acostumbrado, por lo que si desea encontrar el valor de la matriz en la posición x,y, diga:
int val = matrix[ x ][ y ];
Esto funciona porque matriz[x] devuelve un puntero a una matriz, que luego se indexa con [y]. Desglosándolo:
int* row = matrix[ x ];
int val = row[ y ];
Conveniente, ¿no? Nos gusta nuestra sintaxis [ x ][ y ].
Pero la solución tiene una gran desventaja , y es que es a la vez gorda y lenta.
¿Por qué?
La razón por la que es a la vez gordo y lento es en realidad la misma. Cada "fila" de la matriz es una matriz dinámica asignada por separado. Hacer una asignación de montón es costoso tanto en tiempo como en espacio. El asignador necesita tiempo para realizar la asignación y, a veces, ejecuta algoritmos O(n) para hacerlo. Y el asignador "rellena" cada una de sus matrices de filas con bytes adicionales para contabilidad y alineación. Ese espacio extra cuesta... bueno... espacio extra. El desasignador también tomará más tiempo cuando vaya a desasignar la matriz, liberando minuciosamente cada asignación de fila individual. Me hace sudar sólo de pensarlo.
Hay otra razón por la que es lento. Estas asignaciones separadas tienden a vivir en partes discontinuas de la memoria. Una fila puede estar en la dirección 1000 y otra en la dirección 100 000; ya se hace una idea. Esto significa que cuando atraviesas la matriz, saltas a través de la memoria como una persona salvaje. Esto tiende a provocar errores de caché que ralentizan enormemente el tiempo de procesamiento.
Entonces, si es absolutamente necesario tener su linda sintaxis de indexación [x][y], use esa solución. Si desea rapidez y pequeñez (y si no le importan, ¿por qué trabaja en C++?), necesita una solución diferente.
Una solución diferente
La mejor solución es asignar toda la matriz como una única matriz dinámica y luego usar sus propias matemáticas de indexación (un poco) inteligentes para acceder a las celdas. Las matemáticas de indexación son sólo un poco inteligentes; No, no es nada inteligente: es obvio.
class Matrix
{
...
size_t index( int x, int y ) const { return x + m_width * y; }
};
Dada esta index()
función (que me imagino que es miembro de una clase porque necesita conocer m_width
su matriz), puede acceder a las celdas dentro de su matriz de matriz. La matriz de matriz se asigna así:
array = new int[ width * height ];
Entonces, el equivalente a esto en la solución lenta y grasa:
array[ x ][ y ]
... ¿está esto en la solución pequeña y rápida?
array[ index( x, y )]
Triste, lo sé. Pero te acostumbrarás. Y tu CPU te lo agradecerá.
En C++11 es posible:
auto array = new double[M][N];
De esta forma, la memoria no se inicializa. Para inicializarlo, haga esto en su lugar:
auto array = new double[M][N]();
Programa de muestra (compilar con "g++ -std=c++11"):
#include <iostream>
#include <utility>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>
using namespace std;
int main()
{
const auto M = 2;
const auto N = 2;
// allocate (no initializatoin)
auto array = new double[M][N];
// pollute the memory
array[0][0] = 2;
array[1][0] = 3;
array[0][1] = 4;
array[1][1] = 5;
// re-allocate, probably will fetch the same memory block (not portable)
delete[] array;
array = new double[M][N];
// show that memory is not initialized
for(int r = 0; r < M; r++)
{
for(int c = 0; c < N; c++)
cout << array[r][c] << " ";
cout << endl;
}
cout << endl;
delete[] array;
// the proper way to zero-initialize the array
array = new double[M][N]();
// show the memory is initialized
for(int r = 0; r < M; r++)
{
for(int c = 0; c < N; c++)
cout << array[r][c] << " ";
cout << endl;
}
int info;
cout << abi::__cxa_demangle(typeid(array).name(),0,0,&info) << endl;
return 0;
}
Producción:
2 4
3 5
0 0
0 0
double (*) [2]
Supongo por su ejemplo de matriz estática que desea una matriz rectangular y no irregular. Puedes utilizar lo siguiente:
int *ary = new int[sizeX * sizeY];
Luego podrás acceder a elementos como:
ary[y*sizeX + x]
No olvides usar eliminar [] en ary
.