Вызовет ли memcpy или memmove проблемы с копированием классов?


Предположим, что у меня есть какой-либо класс или структура. Никаких виртуальных функций или чего-либо еще, только некоторые пользовательские конструкторы, а также несколько указателей, которые потребовали бы очистки в деструкторе.

Будут ли какие-либо неблагоприятные последствия для использования memcpy или memmove в этой структуре? Вызовет ли удаление перемещенной структуры проблемы? Вопрос предполагает, что выравнивание памяти также правильно, и мы копируем в безопасную память.

5 4

5 ответов:

В общем случае, да, будут проблемы. И memcpy, и memmove являются побитовыми операциями без дальнейшей семантики. Этого может быть недостаточно, чтобыпереместить объект.*, и этого явно недостаточно, чтобы скопировать .

В случае копирования он будет нарушен, так как несколько объектов будут ссылаться на одну и ту же динамически выделяемую память, и более одного деструктора попытаются освободить его. Обратите внимание, что такие решения, как shared_ptr, здесь не помогут, так как общий доступ владение является частью дальнейшей семантики , которая memcpy/memmove не предлагай.

Для перемещения , и в зависимости от типа вам это может сойти с рук в некоторых случаях. Но это не будет работать, если объекты содержат указатели / ссылки на перемещаемые элементы (включая само-ссылки), поскольку указатели будут битово скопированы (опять же, нет дальнейшей семантики копирования/перемещения) и будут ссылаться на старые местоположения.

Общий ответ все тот же: не надо.


* Не берите move здесь в точном смысле C++11. Я видел реализацию стандартных библиотечных контейнеров, которые использовали специальные теги, позволяющие перемещать объекты при росте буферов с помощью memcpy, но для этого требовались явные аннотации в сохраненных типах, которые помечали объекты как безопасно перемещаемые через memcpy, после того как объекты были помещены в новый буфер старый буфер был удален без вызова каких-либо деструкторов (C++11 move требует оставить объект в разрушаемом состоянии, которое не может быть достигнуто с помощью этого взлома)

Как правило, использование memcpy для объекта, основанного на классе, не является хорошей идеей. Наиболее вероятной проблемой будет копирование указателя, а затем его удаление. Вместо этого следует использовать конструктор копирования или оператор присваивания.

Нет, не делай этого.

Если вы memcpy структура, деструктор которой удаляет указатель внутри себя, вы будете делать двойное удаление, когда второй экземпляр структуры будет уничтожен любым способом.

Идиома C++ - это конструктор копирования для классов и std::copy или любой из его друзей для копирования диапазонов/последовательностей/контейнеров.

Помимо безопасности, которая является наиболее важным вопросом, как уже указывалось в других ответах, может также возникнуть проблема производительности, особенно для небольших объектов.

Даже для простых типов POD вы можете обнаружить, что выполнение правильной инициализации в списке инициализаторов вашего конструктора копирования (или присваиваний в операторе присваивания в зависимости от вашего использования) на самом деле быстрее, чем даже внутренняя версия memcpy. Это вполне может быть связано с кодом входа memcpy который может проверять выравнивание слов, перекрытия, права доступа к буферу / памяти и т. д.... В Visual C++ 10.0 и выше, чтобы дать вам конкретный пример, Вы были бы удивлены, сколько кода преамбулы, который тестирует различные вещи выполняется до того, как memcpy даже начинает свою логическую функцию.

Если вы используете C++11, вы можете использовать std::is_trivially_copyable, чтобы определить, можно ли скопировать или переместить объект с помощью memcpy или memmove. Из документации:

Объекты тривиально копируемых типов являются единственными объектами C++, которые могут безопасно копировать с помощью std:: memcpy или сериализовать в / из двоичных файлов с помощью std:: ofstream:: write()/std::ifstream:: read(). В общем, а тривиально копируемый тип-это любой тип, для которого базовые байты могут быть скопированы в массив символов char или unsigned char и в новый объект того же типа, и результирующий объект будет иметь то же значение как оригинал.

Многие классы не подходят под это описание, и вы должны остерегаться, что классы могут меняться. Я бы предложил, что если вы собираетесь использовать memcpy/memmove на объектах C++, которые вы каким-то образом защищаете от нежелательного использования. Например, если вы реализуете контейнерный класс, то тип, который содержит контейнер, легко изменить, чтобы он не был более длинные тривиально копируемые (напр. кто-то добавляет виртуальную функцию). Вы можете сделать это с помощью static_assert:

template<typename T>
class MemcopyableArray
{
    static_assert(std::is_trivially_copyable<T>::value, "MemcopyableArray used with object type that is not trivially copyable.");
    // ...
};