C ++ Возврат и вставка объекта 2D-массива

Я пытаюсь вернуть элемент данных массива из одного меньшего объекта 2D-массива и пытаюсь вставить массив в более крупный объект 2D-массива. Но пытаясь это сделать, я столкнулся с двумя проблемами.

Первая проблема заключается в том, что я хочу вернуть имя 2D-массива, но я не знаю, как правильно использовать синтаксис для возврата имени 2D-массива.

Так выглядит мой элемент данных 2D-массива

private:
int pieceArray[4][4];
// 2D Smaller Array

и я хочу вернуть этот массив в функцию, но это вызывает ошибку компилятора:

int Piece::returnPiece()
{
    return pieceArray; //not vaild
    // return the 2D array name
}

Я устал использовать этот тип возврата, и он сработал:

int Piece::returnPiece()
{
    return pieceArray[4][4];
}

Но я не уверен, что это то, что я хочу, поскольку я хочу вернуть массив и все его содержимое.

Другая проблема - это функция InsertArray (), где я бы поместил функцию returnPiece () в аргумент InsertArray ().

Проблема с InsertArray () - это аргумент, вот код для него:

void Grid::InsertArray( int arr[4][4] ) //Compiler accepts, but does not work
{
    for(int i = 0; i < x_ROWS ; ++i)
    {
         for (int j = 0; j < y_COLUMNS ; ++j)
         {
             squares[i][j] = arr[i][j];
         }
    }
}

Проблема в том, что он не принимает мой returnPiece (), и если я удалю «[4] [4]», мой компилятор не примет.

В основном все это синтаксические ошибки, но как мне решить эти проблемы?

  1. Возврат целого массива в returnPiece ()
  2. Правильный синтаксис аргумента в InsertArray ()
  3. Аргумент InsertArray (), принимающий returnPiece ()

Это 3 основные проблемы, с которыми мне нужна помощь, и у меня возникла та же проблема, когда я пытаюсь использовать метод указателя указателя. Кто-нибудь знает, как решить эти 3 проблемы?


person Community    schedule 31.12.2008    source источник


Ответы (4)


Передавая свой массив, вы должны решить, хотите ли вы сделать копию массива или просто хотите вернуть указатель на массив. Для возврата массивов вы не можете (легко) вернуть копию - вы можете вернуть только указатель (или ссылку в C ++). Например:

// Piece::returnPiece is a function taking no arguments and returning a pointer to a
// 4x4 array of integers
int (*Piece::returnPiece(void))[4][4]
{
    // return pointer to the array
    return &pieceArray;
}

Чтобы использовать его, назовите его так:

int (*arrayPtr)[4][4] = myPiece->returnPiece();
int cell = (*arrayPtr)[i][j];  // cell now stores the contents of the (i,j)th element

Обратите внимание на сходство между объявлением типа и его использованием - круглые скобки, оператор разыменования * и квадратные скобки находятся в одних и тех же местах.

Ваше объявление для Grid::InsertArray правильно - оно принимает один аргумент, который представляет собой массив целых чисел 4x4. Это вызов по значению: всякий раз, когда вы вызываете его, вы делаете копию своего массива 4x4, поэтому любые вносимые вами изменения не отражаются в переданном массиве. Если вы вместо этого хотите использовать вызов по ссылке, вы можете вместо этого передайте указатель на массив:

// InsertArray takes one argument which is a pointer to a 4x4 array of integers
void Grid::InsertArray(int (*arr)[4][4])
{
     for(int i = 0; i < x_ROWS; i++)
     {
         for(int j = 0; j < y_COLUMNS ; j++)
             squares[i][j] = (*arr)[i][j];
     }
}

Эти объявления типов с указателями на многомерные массивы могут быстро запутать. Я рекомендую сделать для него typedef вот так:

// Declare IntArray4x4Ptr to be a pointer to a 4x4 array of ints
typedef int (*IntArray4x4Ptr)[4][4];

Затем вы можете объявить свои функции более удобочитаемыми:

IntArray4x4Ptr Piece::returnPiece(void) { ... }
void Grid::InsertArray(IntArray4x4Ptr arr) { ... }

Вы также можете использовать программу cdecl, чтобы помочь расшифровать сложный C / Типы C ++.

person Adam Rosenfield    schedule 31.12.2008
comment
Вот это да. Я понятия не имел, что вы можете вернуть 2d-массив. K&R, кажется, не против. Мне пришлось изменить объявление на: int (* returnPiece ()) [4] {return pieceArray; } - person ; 31.12.2008
comment
да. если вы хотите, чтобы массив распался, вы можете вернуть кусок массива вместо его адреса. будет возвращен указатель на его первый элемент, который имеет тип int (*) [4]. но обратная сторона - вы теряете размер внешнего измерения. я думаю, что подход Адама лучше ... - person Johannes Schaub - litb; 31.12.2008
comment
... если только внешний размер не может иметь произвольный размер. но кажется, что он всегда имеет размер 4. Преимущество возврата распавшегося массива заключается в том, что у вас есть естественный синтаксис iindex для возвращаемого объекта: foo () [4] [4], а не (* foo ()) [4] [ 4]. Принимая во внимание и то, и другое, я бы все равно предпочел Адама. - person Johannes Schaub - litb; 31.12.2008
comment
Нет, его исходный Grid :: InsertArray (int arr [4] [4]) является вызовом по ссылке. В памяти есть только один массив - pieceArray, и он является общим. Ценности можно менять из-под вас. Для Call-by-Value (копирование) ... Лучший чит со структурой. (Я выложу отдельно.) - person Mr.Ree; 01.01.2009

Похоже, вам нужно больше узнать об указателях в C ++ и о передаче по ссылке или передаче по значению.

Ваш метод returnPiece определяется как возвращающий значение одной ячейки. Учитывая индекс (например, [4] [4]), вы возвращаете копию содержимого этой ячейки, поэтому вы не сможете его изменить, или, вернее, изменение его приведет к изменению копии.

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

person Uri    schedule 31.12.2008

Вот как бы я это сделал:

class Array {
  public:

    Array() {
      for (int i = 0; i < 4; ++i)
      {
         for (int j = 0; j < 4; ++j)
         {
            (*this)(i, j) = 0;
         }
      }
    }


    int &operator()(int i, int j)
    {
      return pieceArray[i][j];
    }

  private:
    int pieceArray[4][4];
};

Затем вы можете сделать что-то вроде:

  Array x; // create 4x4 array
  x(1, 2) = 3; // modify element
person Community    schedule 31.12.2008
comment
Более естественно, не следует ли поменять местами i и j? (И, возможно, вместо этого используйте x и y для удобства чтения.) Кроме того, я думаю, что конструктор копирования в порядке ... - person strager; 31.12.2008
comment
i и j позиционны, поэтому вы можете использовать любой порядок, который хотите. Было бы неплохо создать конструктор копирования. - person ; 31.12.2008

Проблема с Адама Розенфилда arrayPtr в том, что Piece :: pieceArray может измениться из-под вас. (Копирование по ссылке vs Копирование по значению.)

Копирование по значению неэффективно. Но если вы действительно хотите это сделать, просто обманите:

struct FOO { int  piece [4][4]; };

FOO Piece::returnPiece()
{
  FOO f;
  memcpy( f.piece, pieceArray, sizeof(pieceArray) );
  return f;
}

void Grid::InsertArray( const FOO & theFoo )
{
  // use theFoo.piece[i][j]
}

Конечно, лучшим, более объектно-ориентированным решением было бы создание и возврат returnPiece () объекта Piece или Array. (Как предложил Хуан ...)

person Mr.Ree    schedule 01.01.2009
comment
общий подход: template ‹typename T› struct wrap {T t; wrap (T const & t) {memcpy (this- ›t, t, sizeof t); }}; шаблон ‹typename T› wrap ‹T› make_wrap (T const & t) {return wrap ‹T› (t); } затем верните make_wrap (pieceArray) и примите wrap ‹int [4] [4]› в методе InsertArray. - person Johannes Schaub - litb; 01.01.2009
comment
litb: Вы подняли хороший вопрос. Но зачем обобщать кладж для преодоления неспособности C / C ++ копировать массивы? make_wrap () не требуется. Будьте осторожны с распространением использования memcpy () на типы, содержащие указатели. Учитывая навыки OP с C ++, разумно ли увеличивать сложность кодирования с помощью шаблонов? - person Mr.Ree; 02.01.2009