¿Cómo declaro una matriz 2D en C++ usando nuevo?

Resuelto user20844 asked hace 15 años • 30 respuestas

¿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.

user20844 avatar Jun 02 '09 03:06 user20844
Aceptado

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= 5y rowCount = 4, produciría lo siguiente:

ingrese la descripción de la imagen aquí

No olvides hacer deletecada fila por separado con un bucle, antes de eliminar la matriz de punteros. Ejemplo en otra respuesta .

Mehrdad Afshari avatar Jun 01 '2009 20:06 Mehrdad Afshari
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]
Kevin Loney avatar Jun 01 '2009 20:06 Kevin Loney

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_widthsu 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á.

Jeff Wofford avatar Mar 03 '2015 20:03 Jeff Wofford

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]
Mohammad Alaggan avatar Apr 26 '2013 14:04 Mohammad Alaggan

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.

Isvara avatar Jun 01 '2009 21:06 Isvara