Pregunta

¿Cómo se asigna dinámicamente una matriz 2D en C ++? Lo he intentado en base a lo que ya sé:

#include <iostream>

int main(){
    int rows;
    int cols;
    int * arr;
    arr = new int[rows][cols];
 }

Funciona para un parámetro, pero ahora para dos. ¿Qué debo hacer?

¿Fue útil?

Solución

Una matriz es en realidad una matriz de matrices.

int rows = ..., cols = ...;
int** matrix = new int*[rows];
for (int i = 0; i < rows; ++i)
    matrix[i] = new int[cols];

Por supuesto, para eliminar la matriz, debe hacer lo siguiente:

for (int i = 0; i < rows; ++i)
    delete [] matrix[i];
delete [] matrix;

Acabo de descubrir otra posibilidad:

int rows = ..., cols = ...;
int** matrix = new int*[rows];
if (rows)
{
    matrix[0] = new int[rows * cols];
    for (int i = 1; i < rows; ++i)
        matrix[i] = matrix[0] + i * cols;
}

Liberar esta matriz es más fácil:

if (rows) delete [] matrix[0];
delete [] matrix;

Esta solución tiene la ventaja de asignar un solo bloque grande de memoria para todos los elementos, en lugar de varios pequeños fragmentos. Sin embargo, la primera solución que publiqué es un mejor ejemplo del concepto arrays of arrays .

Otros consejos

También puede usar std::vectors para lograr esto:

usando std::vector< std::vector<int> >

Ejemplo:

std::vector< std::vector<int> > a;

  //m * n is the size of the matrix

    int m = 2, n = 4;
    //Grow rows by m
    a.resize(m);
    for(int i = 0 ; i < m ; ++i)
    {
        //Grow Columns by n
        a[i].resize(n);
    }
    //Now you have matrix m*n with default values

    //you can use the Matrix, now
    a[1][0]=1;
    a[1][1]=2;
    a[1][2]=3;
    a[1][3]=4;

//OR
for(i = 0 ; i < m ; ++i)
{
    for(int j = 0 ; j < n ; ++j)
    {      //modify matrix
        int x = a[i][j];
    }

}

Pruebe boost :: multi_array

#include <boost/multi_array.hpp>

int main(){
    int rows;
    int cols;
    boost::multi_array<int, 2> arr(boost::extents[rows][cols] ;
}
 #include <iostream>

    int main(){
        int rows=4;
        int cols=4;
        int **arr;

        arr = new int*[rows];
        for(int i=0;i<rows;i++){
           arr[i]=new int[cols];
        }
        // statements

        for(int i=0;i<rows;i++){
           delete []arr[i];
        }
        delete []arr;
        return 0;
     }
arr = new int[cols*rows];

Si tampoco te importa la sintaxis

arr[row * cols + col] = Aij;

o use el operador [] sobrecargando en alguna parte. Esto puede ser más amigable con la memoria caché que la matriz de matrices, o puede no serlo, lo más probable es que no le importe. Solo quiero señalar que a) la matriz de matrices no es solo la solución, b) algunas operaciones son más fáciles de implementar si la matriz se encuentra en un bloque de memoria. Por ejemplo,

for(int i=0;i < rows*cols;++i)
   matrix[i]=someOtherMatrix[i];

una línea más corta que

for(int r=0;i < rows;++r)
  for(int c=0;i < cols;++s)
     matrix[r][c]=someOtherMatrix[r][c];

aunque agregar filas a dicha matriz es más doloroso

o simplemente puede asignar una matriz 1D pero elementos de referencia en forma 2D:

para abordar la fila 2, columna 3 (la esquina superior izquierda es la fila 0, columna 0):

arr [2 * MATRIX_WIDTH + 3]

donde MATRIX_WIDTH es el número de elementos en una fila.

La otra respuesta que describe las matrices de matrices es correcta.
PERO si está planeando hacer algo matemático con las matrices, o si necesita algo especial como matrices dispersas, debe mirar una de las muchas bibliotecas de matemáticas como TNT antes de reinventar demasiadas ruedas

Tengo esta clase de cuadrícula que se puede usar como una matriz simple si no necesita ningún operador matemático.

/**
 * Represents a grid of values.
 * Indices are zero-based.
 */
template<class T>
class GenericGrid
{
    public:
        GenericGrid(size_t numRows, size_t numColumns);

        GenericGrid(size_t numRows, size_t numColumns, const T & inInitialValue);

        const T & get(size_t row, size_t col) const;

        T & get(size_t row, size_t col);

        void set(size_t row, size_t col, const T & inT);

        size_t numRows() const;

        size_t numColumns() const;

    private:
        size_t mNumRows;
        size_t mNumColumns;
        std::vector<T> mData;
};


template<class T>
GenericGrid<T>::GenericGrid(size_t numRows, size_t numColumns):
    mNumRows(numRows),
    mNumColumns(numColumns)
{
    mData.resize(numRows*numColumns);
}


template<class T>
GenericGrid<T>::GenericGrid(size_t numRows, size_t numColumns, const T & inInitialValue):
    mNumRows(numRows),
    mNumColumns(numColumns)
{
    mData.resize(numRows*numColumns, inInitialValue);
}


template<class T>
const T & GenericGrid<T>::get(size_t rowIdx, size_t colIdx) const
{
    return mData[rowIdx*mNumColumns + colIdx];
}


template<class T>
T & GenericGrid<T>::get(size_t rowIdx, size_t colIdx)
{
    return mData[rowIdx*mNumColumns + colIdx];
}


template<class T>
void GenericGrid<T>::set(size_t rowIdx, size_t colIdx, const T & inT)
{
    mData[rowIdx*mNumColumns + colIdx] = inT;
}


template<class T>
size_t GenericGrid<T>::numRows() const
{
    return mNumRows;
}


template<class T>
size_t GenericGrid<T>::numColumns() const
{
    return mNumColumns;
}

El uso del doble puntero es, con mucho, el mejor compromiso entre velocidad de ejecución / optimización y legibilidad. Usar una sola matriz para almacenar el contenido de la matriz es en realidad lo que hace un puntero doble.

He utilizado con éxito la siguiente función de creador con plantilla (sí, sé que uso referencias de puntero estilo C antiguas, pero hace que el código sea más claro en el lado de la llamada con respecto a los parámetros cambiantes, algo que me gusta de los punteros que es no es posible con referencias. Verá lo que quiero decir):

///
/// Matrix Allocator Utility
/// @param pppArray Pointer to the double-pointer where the matrix should be allocated.
/// @param iRows Number of rows.
/// @param iColumns Number of columns.
/// @return Successful allocation returns true, else false.
template <typename T>
bool NewMatrix(T*** pppArray, 
               size_t iRows, 
               size_t iColumns)
{
   bool l_bResult = false;
   if (pppArray != 0) // Test if pointer holds a valid address.
   {                  // I prefer using the shorter 0 in stead of NULL.
      if (!((*pppArray) != 0)) // Test if the first element is currently unassigned.
      {                        // The "double-not" evaluates a little quicker in general.
         // Allocate and assign pointer array.
         (*pppArray) = new T* [iRows]; 
         if ((*pppArray) != 0) // Test if pointer-array allocation was successful.
         {
            // Allocate and assign common data storage array.
            (*pppArray)[0] = new T [iRows * iColumns]; 
            if ((*pppArray)[0] != 0) // Test if data array allocation was successful.
            {
               // Using pointer arithmetic requires the least overhead. There is no 
               // expensive repeated multiplication involved and very little additional 
               // memory is used for temporary variables.
               T** l_ppRow = (*pppArray);
               T* l_pRowFirstElement = l_ppRow[0];
               for (size_t l_iRow = 1; l_iRow < iRows; l_iRow++)
               {
                  l_ppRow++;
                  l_pRowFirstElement += iColumns;
                  l_ppRow[0] = l_pRowFirstElement;
               }
               l_bResult = true;
            }
         }
      }
   }
}

Para desasignar la memoria creada usando la utilidad mencionada anteriormente, uno simplemente tiene que desasignar a la inversa.

///
/// Matrix De-Allocator Utility
/// @param pppArray Pointer to the double-pointer where the matrix should be de-allocated.
/// @return Successful de-allocation returns true, else false.
template <typename T>
bool DeleteMatrix(T*** pppArray)
{
   bool l_bResult = false;
   if (pppArray != 0) // Test if pointer holds a valid address.
   {
      if ((*pppArray) != 0) // Test if pointer array was assigned.
      {
         if ((*pppArray)[0] != 0) // Test if data array was assigned.
         {
               // De-allocate common storage array.
               delete [] (*pppArray)[0];
            }
         }
         // De-allocate pointer array.
         delete [] (*pppArray);
         (*pppArray) = 0;
         l_bResult = true;
      }
   }
}

Usar estas funciones de plantilla mencionadas anteriormente es muy fácil (por ejemplo):

   .
   .
   .
   double l_ppMatrix = 0;
   NewMatrix(&l_ppMatrix, 3, 3); // Create a 3 x 3 Matrix and store it in l_ppMatrix.
   .
   .
   .
   DeleteMatrix(&l_ppMatrix);
const int nRows = 20;
const int nCols = 10;
int (*name)[nCols] = new int[nRows][nCols];
std::memset(name, 0, sizeof(int) * nRows * nCols); //row major contiguous memory
name[0][0] = 1; //first element
name[nRows-1][nCols-1] = 1; //last element
delete[] name;

Aquí está el amplificador & más claro; De manera intuitiva, sé asignar una matriz dinámica 2d en C ++. En este ejemplo, la plantilla cubre todos los casos.

template<typename T> T** matrixAllocate(int rows, int cols, T **M)
{
    M = new T*[rows];
    for (int i = 0; i < rows; i++){
        M[i] = new T[cols];
    }
    return M;
}

... 

int main()
{
    ...
    int** M1 = matrixAllocate<int>(rows, cols, M1);
    double** M2 = matrixAllocate(rows, cols, M2);
    ...
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top