2011-01-11 2 views
0

первый вопрос:элегантный способ создания и пропускания многомерного массива в C++?

для известных размеров, нам не нужны новые/таНос для создания

const int row = 3; 
    const int col = 2; 
    int tst_matrix[row][col] ={{1,2},{3,4},{5,6}} 

однако, не легко пройти этот двумерный массив в другую функцию, не так ли? потому что

int matrix_process(int in_matrix[][]) 

является незаконным, вы должны указать все размеры, кроме первого. если мне нужно изменить содержимое in_matrix, как я могу легко передать tst_matrix в функцию matrix_process?

второй вопрос: Какой стандартный способ создания 2-мерного массива в C++ с новым? Я не хочу использовать std :: vector и т. Д. Здесь. вот что я придумал, это лучший способ?

int **tst_arr = new int*[5]; 
    int i=0, j=0; 

    for (i=0;i<5;i++) 
    { 
     tst_arr[i] = new int[5]; 
     for (j=0;j<5;j++) 
     { 
      tst_arr[i][j] = i*5+j; 
     } 
    } 

Кроме того, если я прохожу tst_array другой функции, как:

 int change_row_col(int **a)  
    { 
     ..................... 
     //check which element is 0 
     for (i=0; i<5; i++) 
      for(j=0;j<5;j++) 
      { 
      if (*(*(a+i)+j)==0) //why I can not use a[i][j] here? 
      { 
       row[i]=1; 
       col[j]=1;    
      } 
      } 
     ..................... 
    } 

Кроме того, если я использую ( (а + I) + J), то результат не то, что Я хочу. Вот полное тестирование кода у меня было:

#include <iostream> 

    using namespace std; 

    //Input Matrix--a: Array[M][N] 

    int change_row_col(int **a) 
    { 
    int i,j; 
    int* row = new int[5]; 
    int* col = new int[5]; 

    //initialization 
    for(i=0;i<5;i++) 
    { 
     row[i]=0; 
    } 

    for(j=0;j<5;i++) 
    { 
     col[j]=0; 
    } 

    //check which element is 0 
    for (i=0; i<5; i++) 
     for(j=0;j<5;j++) 
     { 
      if (*(*(a+i)+j)==0) //why I can not use a[i][j] here? 
      { 
       row[i]=1; 
       col[j]=1;    
      } 
     } 

    for(i=0;i<5;i++) 
     for (j=0;j<5;j++) 
     { 
      if (row[i] || col[j])  
      { 
       *(*(a+i)+j)=0; 
      } 
     } 
    return 1; 
} 



int main() 
{ 
    int **tst_arr = new int*[5]; 
    int i=0, j=0; 

    for (i=0;i<5;i++) 
    { 
     tst_arr[i] = new int[5]; 
     for (j=0;j<5;j++) 
     { 
      tst_arr[i][j] = i*5+j; 
     } 
    } 

    for (i=0; i<5;i++) 
    { 
    for(j=0; j<5;j++) 
    { 
     cout<<" "<<tst_arr[i][j]; 
    } 
    cout<<endl; 
    } 
change_row_col(tst_arr); 

for (i=0; i<5;i++) 
{ 
    for(j=0; j<5;j++) 
    { 
     cout<<" "<<tst_arr[i][j]; 
    } 
    cout<<endl; 
} 

    for (i=0;i<5;i++) 
    { 
     delete []tst_arr[i]; 
    } 
    delete []tst_arr; 
} 
+6

Почему вы не хотите использовать 'зЬй :: VECTOR'? –

+2

«Я не хочу использовать std :: vector» У вас есть все основания для этого? – Simone

+2

Ну, если вы не хотите использовать std :: vector, всегда есть boost :: array. – stefaanv

ответ

1

Моего решения с использованием шаблона функции:

template<size_t M,size_t N> 
void Fun(int (&arr)[M][N]) 
{ 
    for (int i = 0 ; i < M ; i++) 
    { 
     for (int j = 0 ; j < N ; j++) 
     { 
      /*................*/ 
     } 
    } 
} 
+0

Это все еще работает только с известными размерами; он работает только с известными измерениями. При измерениях, определенных во время выполнения, это не решение. –

+0

@ Чарльз: Я полностью согласен. Для размеров, известных во время выполнения, нужно выбрать либо то, что вы предложили, либо написать собственный шаблон класса «Array <>» (используя также некоторые метапрограммы). В прошлом году мой последний проект потребовал от меня написать это, поэтому я написал 'Array <>', чтобы выполнить мою работу. – Nawaz

3

Для многомерных массивов были все границы изменчивы во время выполнения, наиболее распространенный подход, который я знаю, это использовать динамически выделяемый одномерный массив и сделать индексные вычисления «вручную». В C++ вы обычно используете класс, такой как специализация std::vector для управления распределением и освобождением этого массива.

Это дает по существу тот же макет, что и многомерный массив с фиксированными границами, и не имеет каких-либо реальных подразумеваемых служебных данных, поскольку без фиксированных границ любой подход потребует передачи всех баров одного из размеров массива во время выполнения.

+0

Вторичный. Подход выделения массива указателей и последующее распределение массивов для каждого указателя, как и в примере кода, представляет собой ужасно неэффективный способ хранения массивов любого размера. Единственное, что я добавлю к вашему ответу, это то, что вы должны определить функцию доступа (я предлагаю 'operator()' с двумя аргументами), который выполняет вычисления индекса, а не делает их вручную при каждом поиске. –

2

Я честно считаю, что лучшая идея состоит в том, чтобы избегать необработанных массивов C++ в пользу класса-оболочки, такого как boost :: multi_array. Это устраняет все виды странности, возникающие при использовании необработанных массивов (трудности с передачей им параметров S на функции, проблемы с отслеживанием размеров массивов и т. Д.)

Кроме того, я настоятельно рекомендую вам пересмотреть свою позицию по std: :вектор. Это гораздо безопаснее, чем необработанные массивы, поэтому в большинстве случаев действительно нет веских причин использовать динамические массивы над векторами. Если у вас есть фон C, стоит потратить время, чтобы сделать переключатель.

+0

+1 для предотвращения синдрома NIH. Также рассмотрим boost :: array. В зависимости от ваших конкретных потребностей может быть совершенно правильным смешивать и сопоставлять std :: vector, boost :: array и boost :: multi_array всеми возможными способами. –

1

1)

template < typename T, size_t Row_, size_t Col_> 
class t_two_dim { 
public: 
    static const size_t Row = Row_; 
    static const size_t Col = Col_; 
    /* ... */ 
    T at[Row][Col]; 
}; 

template <typename T> 
int matrix_process(T& in_matrix) { 
    return T::Row * T::Col + in_matrix.at[0][0]; 
} 

2) использовать зЬй :: вектор. вы добавляете несколько вызовов функций (которые могут быть встроены в оптимизированную сборку) и могут экспортировать несколько дополнительных символов. я предполагаю, что есть очень веские причины, чтобы избежать этого, но соответствующие обоснования настолько редко выглядят. есть ли у вас соответствующее обоснование?

1

Простой ответ заключается в том, что элегантный способ сделать это в C++ (вы помечены C и C++, но ваш код C++ new/delete) является созданием двумерный matrix класса и передать вокруг (по ссылке или константной ссылке).После этого следующая опция всегда должна быть std::vector (и, опять же, я бы реализовал матричный класс в терминах вектора). Если у вас нет очень веских причин для этого, я бы избегал иметь дело с необработанными массивами массивов.

Если вам действительно нужно, но только если вам действительно нужно, вы можете отлично работать с многомерными массивами, это просто немного громоздко, чем с обычными массивами. Если все параметры известны во время компиляции, как в вашем первом блоке, это некоторые из параметров.

const unsigned int dimX = ...; 
const unsigned int dimY = ...; 
int array[dimY][dimX]; 
void foo(int *array[dimX], unsigned int dimy); // [1] 
void foo(int (&array)[dimY][dimX]);   // [2] 

В работе [1], используя pass-by-value синтаксис массива затухает в указатель на первый элемент, который означает указатель в качестве int [dimX], и это то, что вам нужно пройти. Обратите внимание, что вы должны передать другое измерение в другом аргументе, так как это будет неизвестно кодом в функции. В [2], передавая в массив ссылку, все измерения могут быть фиксированными и известными. Компилятор гарантирует, что вы вызываете только правильный размер массива (оба измерения совпадают) и, следовательно, не нужно передавать дополнительный параметр. Второй вариант может быть шаблонный для размещения для различных размеров (все они известны во время компиляции):

template <unsigned int DimX, unsigned int DimY> 
void foo(int (&array)[DimY][DimX]); 

компилятор будет вычитать размеры (если реальный массив передается в шаблон) и вы иметь возможность использовать его внутри шаблона как DimX и DimY. Это позволяет использовать функцию с разными размерами массива, если они известны во время компиляции.

Если измерения не известны во время компиляции, все становится довольно беспорядочным, и единственный разумный подход заключается в инкапсуляции матрицы в классе. Есть в основном два подхода. Первый - это выделение единого непрерывного блока памяти (как это делал компилятор в предыдущих случаях), а затем предоставление функций, которые индексируют этот блок двумя измерениями. Посмотрите на ссылку в первом абзаце для простого подхода, даже если я буду использовать std::vector вместо внутреннего указателя. Обратите внимание, что с необработанным указателем вам необходимо вручную управлять удалением указателя при уничтожении, или ваша программа будет утечка памяти.

Другой подход, который вы начали во второй части вашего вопроса, является тем, который я бы избегал любой ценой, и состоит в том, чтобы держать указатель на блок указателей на целые числа. Это усложняет управление памятью (вы перешли от delete к указателю на удаление DimY+1 указателей --each array[i], плюс array), и вам также необходимо вручную гарантировать при распределении, что все строки содержат одинаковое количество столбцов. Существенное увеличение числа вещей, которые могут пойти не так, и не получить выигрыш, но некоторые фактические потери (больше памяти требуется для хранения промежуточных указателей, худшая производительность во время выполнения, поскольку вам нужно удвоить ссылку, возможно, худшую локальность данных ...

Завершение: напишите класс, который инкапсулирует двумерный объект в виде непрерывного блока памяти (массив, если размеры известны во время компиляции, - запишите шаблон для разных размеров времени компиляции--, std::vector, если размеры не являются известная до выполнения, указатель, только если у вас есть веские основания для этого) и передать этот объект. Любая другая вещь будет чаще, чем не просто усложнять ваш код и сделать его более подверженным ошибкам.

0

Для вашего первого вопроса:

Если вам нужно передать массив ND с переменным размером вы можете следовать следующему методу для определения такой функции. Таким образом, вы можете передать необходимые аргументы размера функции. Я проверил это в gcc, и он работает.

Пример для 2D случая:

void editArray(int M,int N,int matrix[M][N]){ 

//do something here 

} 
int mat[4][5]; 

editArray(4,5,mat); //call in this way 
Смежные вопросы