Динамический двумерный массив с указателем на указатель
Первый таймер на этом веб-сайте, поэтому здесь идет.
Я новичок на С++, и сейчас я работаю над книгой "Структуры данных, использующие С++ 2nd ed, D.S. Malik".
В книге Малик предлагает два способа создания динамического двумерного массива.
В первом методе вы объявляете переменную как массив указателей, где каждый указатель имеет тип integer. напр.
int *board[4];
.. и затем использовать for-loop для создания "столбцов" при использовании массива указателей как "строк".
Второй метод, вы используете указатель на указатель.
int **board;
board = new int* [10];
и др.
Мой вопрос таков: какой метод лучше? Метод ** мне легче визуализировать, но первый метод можно использовать почти так же. Оба способа можно использовать для создания динамических 2-мерных массивов.
Изменить: не было достаточно ясно, как указано выше. Вот какой код я пробовал:
int row, col;
cout << "Enter row size:";
cin >> row;
cout << "\ncol:";
cin >> col;
int *p_board[row];
for (int i=0; i < row; i++)
p_board[i] = new int[col];
for (int i=0; i < row; i++)
{
for (int j=0; j < col; j++)
{
p_board[i][j] = j;
cout << p_board[i][j] << " ";
}
cout << endl;
}
cout << endl << endl;
int **p_p_board;
p_p_board = new int* [row];
for (int i=0; i < row; i++)
p_p_board[i] = new int[col];
for (int i=0; i < row; i++)
{
for (int j=0; j < col; j++)
{
p_p_board[i][j] = j;
cout << p_p_board[i][j] << " ";
}
cout << endl;
}
Ответы
Ответ 1
Первый метод нельзя использовать для создания динамических 2D-массивов, потому что:
int *board[4];
вы по существу выделили массив из 4 указателей на int
на стек. Поэтому, если вы теперь заполняете каждый из этих 4 указателей динамическим массивом:
for (int i = 0; i < 4; ++i) {
board[i] = new int[10];
}
то, что вы заканчиваете, представляет собой 2D-массив с статическим числом строк (в данном случае 4) и динамическим числом столбцов (в данном случае 10). Таким образом, динамика не полностью, так как при распределении массива в стеке вы должны указывать постоянный размер, т.е. Известный в время. Динамический массив называется динамическим, потому что его размер не обязательно должен быть известен в время компиляции, но скорее может быть определен некоторой переменной в во время выполнения.
Еще раз, когда вы выполните:
int *board[4];
или
const int x = 4; // <--- `const` qualifier is absolutely needed in this case!
int *board[x];
вы предоставляете константу, известную в время компиляции (в данном случае 4 или x
), чтобы компилятор теперь мог предварительно выделить эту память для вашего массива, и когда ваша программа будет загружена в память, у нее уже будет этот объем памяти для массива board
, поэтому он называется static, т.е. потому что размер жестко закодирован и не могут динамически меняться (во время выполнения).
С другой стороны, когда вы делаете:
int **board;
board = new int*[10];
или
int x = 10; // <--- Notice that it does not have to be `const` anymore!
int **board;
board = new int*[x];
компилятор не знает, сколько потребуется массиву памяти board
, и поэтому он не заранее выделяет все. Но когда вы запускаете свою программу, размер массива будет определяться значением переменной x
(во время выполнения), а соответствующее пространство для массива board
будет выделено на так называемую кучу - область памяти, в которой все программы, запущенные на вашем компьютере, могут выделять неизвестно заранее (во время компиляции) суммирует память для личного использования.
В результате, чтобы действительно создать динамический 2D-массив, вам нужно пойти со вторым методом:
int **board;
board = new int*[10]; // dynamic array (size 10) of pointers to int
for (int i = 0; i < 10; ++i) {
board[i] = new int[10];
// each i-th pointer is now pointing to dynamic array (size 10) of actual int values
}
Мы только что создали квадратный 2D-массив размером 10 на 10. Чтобы пройти его и заполнить его фактическими значениями, например 1, мы могли бы использовать вложенные циклы:
for (int i = 0; i < 10; ++i) { // for each row
for (int j = 0; j < 10; ++j) { // for each column
board[i][j] = 1;
}
}
Ответ 2
То, что вы описываете для второго метода, дает только 1D-массив:
int *board = new int[10];
Это просто выделяет массив с 10 элементами. Возможно, вы имели в виду что-то вроде этого:
int **board = new int*[4];
for (int i = 0; i < 4; i++) {
board[i] = new int[10];
}
В этом случае мы выделяем 4 int*
, а затем каждый из них укажем на динамически выделенный массив из 10 int
s.
Итак, теперь мы сравниваем это с int* board[4];
. Основное различие заключается в том, что при использовании такого массива количество "строк" должно быть известно во время компиляции. Это потому, что массивы должны иметь фиксированные размеры времени компиляции. У вас может также возникнуть проблема, если вы хотите, возможно, вернуть этот массив из int*
s, поскольку массив будет уничтожен в конце его области.
Метод, в котором динамически распределяются как строки, так и столбцы, требует более сложных мер, чтобы избежать утечек памяти. Вы должны освободить память так:
for (int i = 0; i < 4; i++) {
delete[] board[i];
}
delete[] board;
Я должен рекомендовать вместо этого использовать стандартный контейнер. Вы можете использовать std::array<int, std::array<int, 10> 4>
или, возможно, std::vector<std::vector<int>>
, который вы инициализируете соответствующим размером.
Ответ 3
В обоих случаях ваше внутреннее измерение может быть динамически задано (т.е. взято из переменной), но разница во внешнем измерении.
Этот вопрос в основном эквивалентен следующему:
Является int* x = new int[4];
"лучше", чем int x[4]
?
Ответ: "нет, если вам не нужно выбирать этот размер массива динамически".
Ответ 4
Этот код хорошо работает с очень небольшим количеством требований к внешним библиотекам и показывает базовое использование int **array
.
Этот ответ показывает, что массив each имеет динамический размер, а также как назначить линейный массив динамически размера в массив ветвей динамического размера.
Эта программа принимает аргументы из STDIN в следующем формате:
2 2
3 1 5 4
5 1 2 8 9 3
0 1
1 3
Код для программы ниже...
#include <iostream>
int main()
{
int **array_of_arrays;
int num_arrays, num_queries;
num_arrays = num_queries = 0;
std::cin >> num_arrays >> num_queries;
//std::cout << num_arrays << " " << num_queries;
//Process the Arrays
array_of_arrays = new int*[num_arrays];
int size_current_array = 0;
for (int i = 0; i < num_arrays; i++)
{
std::cin >> size_current_array;
int *tmp_array = new int[size_current_array];
for (int j = 0; j < size_current_array; j++)
{
int tmp = 0;
std::cin >> tmp;
tmp_array[j] = tmp;
}
array_of_arrays[i] = tmp_array;
}
//Process the Queries
int x, y;
x = y = 0;
for (int q = 0; q < num_queries; q++)
{
std::cin >> x >> y;
//std::cout << "Current x & y: " << x << ", " << y << "\n";
std::cout << array_of_arrays[x][y] << "\n";
}
return 0;
}
Это очень простая реализация int main
и зависит только от std::cin
и std::cout
. Barebones, но достаточно хорошо, чтобы показать, как работать с простыми многомерными массивами.
Ответ 5
это можно сделать следующим образом
- Я использовал перегрузку оператора
- Перегруженное назначение
Конструктор перегруженного копирования
/*
* Soumil Nitin SHah
* Github: https://github.com/soumilshah1995
*/
#include <iostream>
using namespace std;
class Matrix{
public:
/*
* Declare the Row and Column
*
*/
int r_size;
int c_size;
int **arr;
public:
/*
* Constructor and Destructor
*/
Matrix(int r_size, int c_size):r_size{r_size},c_size{c_size}
{
arr = new int*[r_size];
// This Creates a 2-D Pointers
for (int i=0 ;i < r_size; i++)
{
arr[i] = new int[c_size];
}
// Initialize all the Vector to 0 initially
for (int row=0; row<r_size; row ++)
{
for (int column=0; column < c_size; column ++)
{
arr[row][column] = 0;
}
}
std::cout << "Constructor -- creating Array Size ::" << r_size << " " << c_size << endl;
}
~Matrix()
{
std::cout << "Destructpr -- Deleting Array Size ::" << r_size <<" " << c_size << endl;
}
Matrix(const Matrix &source):Matrix(source.r_size, source.c_size)
{
for (int row=0; row<source.r_size; row ++)
{
for (int column=0; column < source.c_size; column ++)
{
arr[row][column] = source.arr[row][column];
}
}
cout << "Copy Constructor " << endl;
}
public:
/*
* Operator Overloading
*/
friend std::ostream &operator<<(std::ostream &os, Matrix & rhs)
{
int rowCounter = 0;
int columnCOUNTER = 0;
int globalCounter = 0;
for (int row =0; row < rhs.r_size; row ++)
{
for (int column=0; column < rhs.c_size ; column++)
{
globalCounter = globalCounter + 1;
}
rowCounter = rowCounter + 1;
}
os << "Total There are " << globalCounter << " Elements" << endl;
os << "Array Elements are as follow -------" << endl;
os << "\n";
for (int row =0; row < rhs.r_size; row ++)
{
for (int column=0; column < rhs.c_size ; column++)
{
os << rhs.arr[row][column] << " ";
}
os <<"\n";
}
return os;
}
void operator()(int row, int column , int Data)
{
arr[row][column] = Data;
}
int &operator()(int row, int column)
{
return arr[row][column];
}
Matrix &operator=(Matrix &rhs)
{
cout << "Assingment Operator called " << endl;cout <<"\n";
if(this == &rhs)
{
return *this;
} else
{
delete [] arr;
arr = new int*[r_size];
// This Creates a 2-D Pointers
for (int i=0 ;i < r_size; i++)
{
arr[i] = new int[c_size];
}
// Initialize all the Vector to 0 initially
for (int row=0; row<r_size; row ++)
{
for (int column=0; column < c_size; column ++)
{
arr[row][column] = rhs.arr[row][column];
}
}
return *this;
}
}
};
int main()
{
Matrix m1(3,3); // Initialize Matrix 3x3
cout << m1;cout << "\n";
m1(0,0,1);
m1(0,1,2);
m1(0,2,3);
m1(1,0,4);
m1(1,1,5);
m1(1,2,6);
m1(2,0,7);
m1(2,1,8);
m1(2,2,9);
cout << m1;cout <<"\n"; // print Matrix
cout << "Element at Position (1,2) : " << m1(1,2) << endl;
Matrix m2(3,3);
m2 = m1;
cout << m2;cout <<"\n";
print(m2);
return 0;
}