Как можно создать массив, содержащий другие массивы, расположенные в одном блоке памяти?

Как можно создать двумерный массив, находящийся в одной области памяти, для использования в классе с возможностью добавления конструктора перемещения и оператора перемещения для избежания копирования данных? У меня есть вариант создания одномерного массива через динамическую память размером равным двумерному, а также класс-помощник для доступа к элементам массива как к двумерному с проверкой индексов. Однако, возможно есть более элегантные способы реализации данной задачи без появления "костылей". Возможно ли создать двумерный массив, используя два динамических массива (один из указателей), где указателям присваиваются адреса участков памяти из другого динамического массива?
  • 19 февраля 2024 г. 11:28
Ответы на вопрос 3
Для создания массива, содержащего другие массивы, расположенных в одном блоке памяти, можно воспользоваться выделением памяти для массива указателей на массивы, а затем выделить память для каждого из массивов и присвоить указатели на них элементам массива указателей. Примерно так:

```cpp
#include <iostream>

int main() {
    const int rows = 3;
    const int cols = 5;

    int** array = new int*[rows]; // массив указателей на строки
    int* data = new int[rows * cols]; // выделение памяти для всех элементов

    for (int i = 0; i < rows; ++i) {
        array[i] = data + i * cols; // присваиваем указатели на каждую строку
    }

    // заполнение и использование массива
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            array[i][j] = i * cols + j;
            std::cout << "array[" << i << "][" << j << "] = " << array[i][j] << std::endl;
        }
    }

    delete[] array;
    delete[] data;

    return 0;
}
```

Чтобы реализовать двумерный массив, находящийся в одном блоке памяти, для использования в классе с возможностью перемещения, можно воспользоваться подходом, предложенным в вашем варианте. Если вы хотите избежать "костылей", можно использовать стандартную библиотеку C++ и контейнер `std::vector` для хранения двумерного массива. 

Например:

```cpp
#include <iostream>
#include <vector>

class Matrix {
private:
    std::vector<std::vector<int>> data;

public:
    Matrix(const std::vector<std::vector<int>>& input_data) : data(input_data) {}

    // конструктор перемещения
    Matrix(Matrix&& other) noexcept : data(std::move(other.data)) {}

    // оператор перемещения
    Matrix& operator=(Matrix&& other) noexcept {
        data = std::move(other.data);
        return *this;
    }

    // метод для доступа к элементам массива как к двумерному
    int& at(int i, int j) {
        return data.at(i).at(j);
    }
};

int main() {
    std::vector<std::vector<int>> input_data = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    Matrix matrix(input_data);

    // пример использования
    std::cout << "matrix[1][1] = " << matrix.at(1, 1) << std::endl;

    return 0;
}
```

В этом примере `std::vector` используется для хранения двумерного массива, что позволяет избежать проблем с управлением памятью и упрощает работу с данными. Класс `Matrix` содержит конструктор перемещения и оператор перемещения для эффективной передачи и перемещения объектов. Метод `at` позволяет обращаться к элементам массива как к двумерному.
Одномерный массив размером X * Y - единственное решение, если нужен непрерывный участок памяти. 
Для получения первого индекса - index / X , для второго - index % X .
Но надо позаботиться - чтобы места было достаточно, иначе однажды получишь OOM либо когда место закончится, либо при сильной фрагментации памяти.

Вариант с "зубчатым" массивом, тоже норм - отложенное выделение можно реализовать. Но по скорости будет проигрывать из-за локальности данных. Хотя, если нужен непрерывный участок - уже не подходит
Есть еще 2 варианта: 

1) Используйте std::vector<std::array> .

2) Что-то среднее между двумя вариантами у вас: как и везде, у вас есть одномерный массив для данных. А operator[] на классе возвращает int* на первый элемент в строке. То же, что у вас, только не надо никакого вспомогательного класса. Таким образом двойная индексация будет работать как надо. Но, как и во втором упомянутом вами примере, тут не будет проверки выхода за границы массива.
Похожие вопросы