2009-09-10 20 views
13

W jaki sposób dynamicznie przydzielasz matrycę 2D w C++? Próbowałem na podstawie tego, co już wiem:Jak dynamicznie alokować macierz?

#include <iostream> 

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

To działa dla jednego parametru, ale teraz dla dwóch osób. Co powinienem zrobić?

+0

zostało zrobione w kółko: http://stackoverflow.com/questions/365782/how -do-i-best-handle-dynamiczne-wielowymiarowe-tablice-in-cc http://stackoverflow.com/questions/527887/cc-optimize-data-structures-array-of-arrays-orjust- array i inne – dmckee

Odpowiedz

41

Macierz to tak naprawdę tablica tablic.

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

Oczywiście, aby usunąć macierz, należy wykonać następujące czynności:

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

Właśnie zorientowali się inną możliwość:

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; 
} 

uwalniając ta tablica jest łatwiej:

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

To rozwiązanie ma tę zaletę, że przydziela pojedynczy duży blok pamięci dla wszystkich elementów, zamiast kilku małych porcji. Pierwsze zamieszczone przeze mnie rozwiązanie jest lepszym przykładem macierzy z tablicami.

+0

To nie jest zły pomysł. Ma oczywistą zaletę polegającą na utrzymywaniu położenia pamięci podręcznej przy zachowaniu wielowymiarowej składni tablic. Lubię to. – greyfade

+0

Po prostu nie miałem pojęcia, jak napisać C++ wtedy ... – pyon

+0

Wolałbym pierwszy, ze względu na jasność. –

2
#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; 
    } 
1

lub po prostu przeznaczyć 1D tablicy ale odniesienia elementy 2D sposób:

z Adresem Rzędu 2, kolumna 3 (górny lewy róg jest rzędu 0, kolumna 0)

arr [2 * MATRIX_WIDTH + 3]

gdzie MATRIX_WIDTH to liczba elementów z rzędu.

+0

Dlaczego próbowałbyś zhackować dwuwymiarową tablicę, gdy jest to całkowicie łatwe do stworzenia prawdziwej dwuwymiarowej tablicy? –

+1

ponieważ można uzyskać dostęp do dwuwymiarowej drogi w sposób przyjazny dla pamięci podręcznej, gdy wydajność jest naprawdę kluczowa. – DeusAduro

+0

Jeszcze lepiej: przydzielić tablicę 1D elementów i inną tablicę 1D wskaźników do pierwszego elementu każdego wiersza. – pyon

1
arr = new int[cols*rows]; 

Jeśli obaj nie przeszkadza składnię

arr[row * cols + col] = Aij; 

lub wykorzystanie operator [] overaloading gdzieś. Może to być bardziej przyjazne dla pamięci podręcznej niż tablica tablic, lub może nie być, prawdopodobnie nie powinno Cię to obchodzić. Chcę tylko zwrócić uwagę, że a) tablica tablic to nie tylko rozwiązanie, b) niektóre operacje są łatwiejsze do wdrożenia, jeśli macierz znajduje się w jednym bloku pamięci. Na przykład.

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

jedna linia krótszy niż

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

chociaż dodanie wierszy do takiej matrycy jest bardziej bolesne

21

Można również użyć std::vectors do osiągnięcia tego celu:

użyciu std::vector< std::vector<int> >

Przykład:

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]; 
    } 

} 
+2

+1 za użycie std :: vector. Nie używam STL zbyt wiele, ale to dlatego, że jestem masochistą, jeśli chodzi o zarządzanie pamięcią. Używanie STL jest mniej podatne na błędy. – pyon

+0

Podczas tej operacji ma powtarzające się ręczne obciążenie. Przygotowałeś klasę (jest ich wiele) lub zawinąłeś tablice n-wymiarowe w stylu c w klasie własnego pomysłu. – dmckee

+6

Zamiast zmieniać rozmiar wystarczy skonstruować go z prawidłowym rozmiarem. 'int m = 10, n = 4; std :: vector > a (m, std :: vector (n, 0)); ' – TimW

14

Spróbuj boost::multi_array

#include <boost/multi_array.hpp> 

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

+1 za użycie funkcji Boost, ale użycie takiej biblioteki zależy od tego, na ile dobry kompilator jest używany optymalizacja kodu. – pyon

0

The innej odpowiedzi opisujące tablice tablic są poprawne.
ale jeśli planowania robi nic matematycznego z tablic - czy trzeba coś specjalnego jak rozrzedzony matryc należy szukać w jednym z wielu matematycznych libs jak TNT przed ponowne wynalezienie zbyt wiele kół

0

mam tej siatki klasa, która może być użyta jako prosta macierz, jeśli nie potrzebujesz żadnych operatorów matematycznych.

/** 
* 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; 
} 
0

Używanie podwójnego wskaźnika jest zdecydowanie najlepszym kompromisem między prędkością/optymalizacją wykonania a czytelnością. Używanie pojedynczej macierzy do przechowywania zawartości macierzy jest właściwie tym, co robi podwójna wskazówka.

Z powodzeniem skorzystałem z funkcji kreatora wzorcowego (tak, wiem, że używam starego wskaźnika w stylu C, odwołującego się, ale dzięki niemu kod jest wyraźniejszy po stronie wzywającej, jeśli chodzi o zmianę parametrów - coś, co podoba mi się w przypadku wskaźników, które nie jest możliwe z odniesieniami Zobaczysz, co mam na myśli).

/// 
/// 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; 
      } 
     } 
     } 
    } 
} 

aby odłączyć przydzielić pamięć utworzonego przy użyciu wyżej wymienionego narzędzia, po prostu ma do de-przeznaczyć w odwrotnej kolejności.

/// 
/// 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; 
     } 
    } 
} 

Aby skorzystać z tych wyżej wymienionych funkcji szablon jest wtedy bardzo proste (np):

. 
    . 
    . 
    double l_ppMatrix = 0; 
    NewMatrix(&l_ppMatrix, 3, 3); // Create a 3 x 3 Matrix and store it in l_ppMatrix. 
    . 
    . 
    . 
    DeleteMatrix(&l_ppMatrix); 
0
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; 
+1

Niektóre wyjaśnienia byłyby po prostu idealne! ;) – mrt