Memset для уникальных указателей

Я конвертирую код C в C++.

Есть матричный указатель:

MATRIX* matrix = NULL;
matrix = new MATRIX[256];

if (matrix == NULL)
   return FAIL;
memset(matrix, 0, 256*sizeof(MATRIX));

Затем он был заполнен по другому методу:

fillUpMatrix(matrix);

И в fillUpMatrix():

memcpy(&matrix[start], &someOtherMatrix[pos], sizeof(MATRIX));

А позже для указателя был вызван memset, так как он будет заполнен другим набором значений:

memset(matrix, 0, 256*sizeof(MATRIX));

Итак, что я сделал:

auto matrix= std::make_unique<MATRIX[]>(256);
fillUpMatrix(matrix.get());

Я пропустил первый memset, так как считаю, что он мне больше не нужен для умных указателей. А вот второй memset считаю нужным (поскольку будут сохраняться новые значения). Итак, как мне написать это на C++, учитывая, что я использую интеллектуальный указатель? Правильно ли мое преобразование выше?

Была ли причина использовать make_unique<MATRIX[]> вместо vector<MATRIX>? Ответ на это может повлиять на ответ на ваш вопрос.

anatolyg 26.06.2019 12:10

Я также думаю, что использование std::vector<Matrix> поможет; тогда вы можете использовать std::copy и т. д., не беспокоясь о sizeof(Matrix)

amlucas 26.06.2019 12:13

Хм, я подумал, раз уж так было MATRIX* matrix = NULL;, то мне следует использовать умный указатель. А если MATRIX matrix, то я буду использовать vector. Это неправильно? И я должен просто использовать вектор? Если мне действительно нужно использовать make_unique, есть предложения?

kzaiwo 26.06.2019 12:13

да, это неверно, вектор хранит свои элементы в куче, поэтому это идеальная замена для MATRIX* matrix, для элементов, выделенных в стеке, эквивалентом будет std::array

463035818_is_not_a_number 26.06.2019 12:20
memset() будет иметь неопределенное поведение, если MATRIX не является тривиально копируемым типом. Это исключает, среди прочего, наличие любого нетривиального конструктора, нетривиального деструктора, любых виртуальных функций или любых виртуальных баз. Для получения дополнительной информации см. en.cppreference.com/w/cpp/named_req/TriviallyCopyable.
Peter 26.06.2019 12:20

Попробуйте вместо этого использовать std::vector или std::array.

Ron 26.06.2019 12:20

afaik std::make_unique<MATRIX[]>(256); для тех случаев, когда вы не можете использовать std;:vector по какой-то причине, когда вы можете, вы должны использовать его

463035818_is_not_a_number 26.06.2019 12:21

Забудьте о memset. Вам это не нужно. Всегда.

Lightness Races in Orbit 26.06.2019 14:23
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
8
1 470
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

  • Использование vector<MATRIX> решит многие проблемы.
  • new не возвращает ноль, а выдает. Следовательно, проверка нуля не имеет никакого эффекта (если не используется nothrow).
  • Вы смешиваете подход необработанных указателей с более новым подходом на основе интеллектуальных указателей, основанным на векторах. Вы не должны использовать какие-либо функции манипулирования необработанной памятью в последнем коде C++.
  • Вместо этого создайте конструктор в классе MATRIX, который будет инициализировать все члены MATRIX. Вы можете иметь (или не допустить) конструкторы копирования/перемещения/операторы присваивания. Рассмотрите возможность использования =default и =delete со специальными функциями-членами.
  • memcpy и т. д. могут испортить состояние некоторых не-членов POD в типе MATRIX (например, std::string, std::vector). Такое неопределенное поведение трудно обнаружить, поэтому не используйте функции памяти*.

это не просто "может испортить", а афаик это УБ

463035818_is_not_a_number 26.06.2019 12:23

да. Я просто сделал это простым.

Ajay 26.06.2019 12:25

Спасибо за предложения! На всякий случай, если я не могу использовать вектор, любое другое предложение, как превратить memset в 0 в код C++? Мне разрешено изменять только эту часть кода, поэтому я не уверен, могу ли я вообще изменить класс MATRIX.

kzaiwo 26.06.2019 12:26

Если memset to MATRIX вызывается/используется во многих местах, я бы предложил использовать вспомогательную функцию. Скажем, ResetMatrix(MATRIX&), который вызовет memset любой другой функции. Вы можете улучшить эту функцию, поместить утверждения во время выполнения, написать модульный тест поверх нее и т. д.

Ajay 26.06.2019 12:29
Ответ принят как подходящий

I skipped the first memset since I believe I do not need it anymore for smart pointers.

Вы правы, что это не нужно, но причина того, что это не нужно, именно в том, что вы использовали std::make_unique, значение которого инициализирует массив; не потому, что вы используете умные указатели. Это предполагает, что инициализация необходима в первую очередь. Похоже, это не так, поскольку содержимое вот-вот будет заполнено функцией.

Учтите, что std::memset и std::memcpy ведут себя корректно, только если тип (в данном случае Matrix) легко копируется. Если это не так, вы должны использовать std::fill_n (или std::fill) и std::copy соответственно. Что можно использовать, если тип также тривиально копируется, поэтому вы можете использовать их в любом случае.

But the second memset I believe is needed (since new values will be saved).

Подобно первому std::memset, непонятно, почему вы думаете, что нужен второй std::memset (будь то в C или C++). Если новые значения будут записаны поверх массива, то какой эффект имеет std::memset?

So how do I write that in C++ and considering that I am using a smart pointer?

Вы можете std::memset массив, на который указывает умный указатель, вот так:

std::memset(matrix.get(), 0, 256*sizeof(decltype(*matrix)));

Или используя std::fill_n вместо этого:

std::fill_n(matrix.get(), 256, MATRIX{});

Hmm my thought was since it was MATRIX* matrix = NULL; then I should use a smart pointer.

std::vector — это контейнер RAII, представляющий собой динамический массив. Вы динамически выделяете массив. std::vector подходит.

Преобразование C в C++ — это не просто замена простых указателей-владельцев интеллектуальными указателями. Еще одна вещь, которую нужно сделать, это заменить пользовательские повторные реализации типичных структур данных и алгоритмов стандартными, предоставляемыми стандартной библиотекой.

Спасибо! Это многое для меня проясняет. Спасибо!

kzaiwo 26.06.2019 12:31

Я думаю, что std::fill_n лучшая альтернатива в таких ситуациях.

sklott 26.06.2019 14:15

Другие вопросы по теме