C : как создать элемент 2D-массива размера, определенного во время выполнения

#c #c 14

#c #c 14

Вопрос:

Я попытался объявить пустой указатель на указатель, присвоить ему указатель на выделенную во время построения память и повторить его, выполнив назначение снова, но я что-то упускаю. Также мне интересно, возможно ли это сделать с std::array помощью . Прикрепление кода:

 // Cell.h

class Cell {
    char contents;
    bool is_free;
};
 
 // Memory.h

#include "Cell.h"
#include <cstddef>

class Memory {
public:
    Memory(std::size_t nlines, std::size_t ncols);
private:
    Cell **cells;
};
 
 // Memory.cpp

#include "Memory.h"

Memory::Memory(std::size_t nlines, std::size_t ncols):
    cells(new Cell[nlines]) // Cannot initialize a member subobject of type 'Cell **' with an rvalue of type 'Cell *'
{
    for (std::size_t i = 0; i < nlines;   i)
        cells[i] = new Cell[ncols];
}
 

Кроме того, мне нужно, чтобы мой массив содержал ячейки со значениями char contents = '.', bool is_free = true после его инициализации. Каков наилучший способ сделать это?

UPD: я подумал о создании 2D псевдомассива с использованием одного указателя (не указателя на указатель) и доступа к ячейке с i*ncols j помощью .

Комментарии:

1. Сообщение об ошибке для инициализации ячеек здесь довольно информативно. Вы пытаетесь присвоить Cell[nlines] (т.Е. Одномерный массив) чему-то типа Cells ** (2D-массив). Вам нужно изменить эту строку на cells(new Cell*[nlines])

Ответ №1:

Также мне любопытно, возможно ли это сделать с помощью std::array

Вы не можете, размер an std::array указан в качестве параметра шаблона, и ваш массив имеет динамический размер.

Вместо того, чтобы иметь дело с необработанными указателями, вам следует std::vector искать массивы с динамическим размером:

 std::vector<std::vector<Cell>> cells{ncols, std::vector{nlines, Cell{}}};
 

Ключевым преимуществом является то, что вам не нужно вручную выделять память и управлять ею.

UPD: я подумал о создании 2D псевдомассива с использованием одного указателя (не указателя на указатель) и доступа к ячейке с помощью i* ncols j .

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

 std::vector<Cell> cells(nlines*ncols, Cell{});
 

Чтобы упростить доступ, вы можете обернуть этот вектор 1D в класс, который предоставляет a operator()(std::size_t row, std::size_t col) , который выполняет преобразование индексов (row, col) в местоположение в массиве 1d.

Комментарии:

1. Спасибо. Также я понял, что мне не нужно перебирать массив и вручную назначать каждую ячейку. Итак new , в c заполняет память элементами со значениями по умолчанию?

2. И есть ли какая-либо разница между инициализацией значений по умолчанию в объявлении класса и его конструкторе?

3. Ваш вопрос мне неясен, фрагмент кода поможет. Если вы имеете в виду, есть ли разница между 1) Cell cell1; и 2) Cell cell2 = Cell{}; , нет, они дают тот же результат для вашего класса.

4. struct Cell { char contents = '.'; bool is_free = true }; и struct Cell { char contents; bool is_free; Cell(char contents = '.', bool is_free = true) :contents(contents), is_free(is_free) { } }; . У меня такое чувство, что они взаимны, но я слышал, что агрегаты не могут иметь инициализаторов, поэтому мне любопытно, есть ли еще какие-либо различия.

5. Ах, я неправильно понял, в этом случае первое предпочтительнее IMO, но это вопрос стиля.

Ответ №2:

Вам нужно написать

 Memory::Memory(std::size_t nlines, std::size_t ncols)
    :cells(new *Cell[nlines]) 
{
...
}
 

(Обратите внимание на Cell * вместо Cell ).

std::array это только фиксированный размер массива во время компиляции, поэтому он не подходит для ваших целей.

Я бы согласился с вашим редактированием. Не используйте массив указателей для матриц, используйте метод плоского массива с индексацией i*ncols j . С этим первым сложнее работать и имеет более высокие накладные расходы (из-за большого количества new вызовов).

Если вы беспокоитесь о накладных расходах на умножение, вы можете создать справочную таблицу для i*ncols . Но я сомневаюсь, что оно того стоит.