#cpp
Суть вопроса. Есть класс: class a { private: int **something; public: a(); ~a(); }; Собственно, как правильно выделять память под двумерный динамический массив? А точнее, где? Некоторые источники указывают на то, что надо не «перегружать» конструктор лишними вычислениями и выделением памяти, а засунуть все в другой метод. a(){something = nullptr;} void doMemory(){ /* выделение памяти */ } ~a(){if(something != nullptr) /* высвобождаем */);} По моим соображениям "засунуть все в другой метод" интересен, если мы создаем больше объекты и они просто лежат в памяти и ждут своего часа, а когда этот час настает, мы берем объект выделяем под него память и сразу работаем с ним, и он дальше лежит ждет своего часа.
Ответы
Ответ 1
Когда выделять? Выделять память нужно в конструкторе, освобождать - в деструкторе. Это - реализация одной из важнейших концепций C++ - RAII (получение ресурса есть инициализация). Если ее нарушить, программа будет либо ненадежной, либо потребуется в каждом методе следить - выделена ли память. Выделять крупный блок или матрицу указателей? Зависит от размеров и от того, что нужно в дальнейшем с этой матрицей делать. Если программа работает с гигабайтами данных, и работает не один раз, а в несколько проходов (выделяя и освобождая память между ними), может оказаться, что адресное пространство сильно фрагментировано, и такого длинного куска, как вам нужно - просто нет. Для 64 бит это не так актуально, но есть на свете и 32 битные системы (в мобильниках и так далее). Если этим данным предстоит параллельная обработка на многоядерной системе с NUMA, а вы выделили память крупным блоком, эта самая NUMA вставит вам палки в колеса, когда процессоры полезут не в свои банки памяти за данными - при выделении построчно вероятность такого будет меньше. Итого: Каждый сценарий обработки данных требует индивидуального выбора способа их хранения.Ответ 2
Начну с конца. Если конструкторов много, то вполне можно вынести общую функциональность в общий метод, и вызывать его в конструкторе (или воспользоваться новой возможностью вызова одного конструктора из другого). Логично и ваше соображение - отложить выделение памяти до того момента, когда она потребуется. Хотя с точки зрения удобства это будет немного хуже - придется все время следить за состоянием, да и удалять, возможно, не в деструкторе, а как только перестанет быть нужной. Но что касается выделения памяти для int**, то оно всегда вызывает у меня вопрос - а надо ли? Зачем вам именно двумерный массив? В конце концов, это тот же одномерный, но с пересчетом индексов. Вам совершенно необходимо обращение к нему как с [row][col]? В конце концов, никто не мешает определить оператор [], который будет возвращать указатель на нужную строку, или operator()(size_t,size_t) и обращаться к элементу массива как к a(row,col). Но это - мое личное мнение, и тут пусть выскажутся профессионалы. Но когда я вижу something = new int*[rows]; for(int i = 0; i < rows; ++i) something[i] = new int[cols]; "у меня начинает чесаться в самых нескромных местах" (с) :) Еще раз - это сугубо мое мнение. Но мне кажется, что something = new int[rows*cols]; менее, так сказать, error-prone...Ответ 3
Мое мнение, определи для двумерного массива свой собственный тип, который будет внутри оперировать низкоуровневыми объектами. Тогда ты сможешь везде его использовать не заботясь о выделении и очистке памяти. RAII - получение ресурса есть инициализация. templateclass DualArray { public: DualArray(int sizeX, int sizeY) : _sizeX(sizeX), _sizeY(sizeY) { _buffer = new T[sizeX * sizeY]; } ~DualArray() { delete _buffer; } void set(int x, int y, const T &t) { _buffer[y * _sizeX + x] = t; } T get(int x, int y) const { return _buffer[y * _sizeX + x]; } private: int _sizeX, _sizeY; T *_buffer; };
Комментариев нет:
Отправить комментарий