Я уже давно работаю с аппаратными API-интерфейсами, и почти все API-интерфейсы, которые я работал, имеют интерфейс C. Таким образом, во многих случаях я работал с голыми new s, небезопасной буферизацией и множеством функций C, завернутых кодом С++. В конце концов, граница между чистым кодом C и чистым кодом С++ была испорчена в моем сознании (и я не знаю, может ли пояснить эту границу вообще).
Теперь, из-за каких-то новых требований к стилю кодирования, мне нужно реорганизовать весь код, который предположительно небезопасен, в более безопасный, написанный на С++ (при условии, что код на С++ будет более безопасным) конечной целью является увеличение кода безопасности с помощью инструментов, которые запускает С++.
Итак, чтобы избавиться от всей моей путаницы, я прошу о помощи по нескольким темам C/С++.
memcpy vs std::copy
AFAIK memcpy - это функция, которая лежит на библиотеках C, поэтому это не С++ ish; с другой стороны std::copy - это функция в STL, поэтому он чистый С++.
- Но это правда? в конце концов,
std::copyвызоветstd::memcpy(в заголовокcstring), если данные тривиально гибны. - Рефакторинг всех вызовов
memcpyв вызовыstd::copyсделает код более "чистым С++".
Чтобы справиться с новыми требованиями стиля кода, я решил продолжить рефакторинг memcpy, есть некоторые вопросы о memcpy и std::copy:
memcpy является небезопасным, потому что он работает с необработанными указателями void, которые могут управлять любым типом указателя независимо от его типа, но в то же время очень гибкие, std::copy не обладает такой гибкостью, гарантирующей безопасность типа. На первый взгляд, memcpy - лучший выбор для работы с процедурами сериализации и десериализации (это действительно мой реальный случай использования), например, для отправки некоторых значений через пользовательскую библиотеку последовательных портов:
void send(const std::string &value)
{
const std::string::size_type Size(value.size());
const std::string::size_type TotalSize(sizeof(Size) + value.size());
unsigned char *Buffer = new unsigned char[TotalSize];
unsigned char *Current = Buffer;
memcpy(Current, &Size, sizeof(Size));
Current += sizeof(Size);
memcpy(Current, value.c_str(), Size);
sendBuffer(Buffer, TotalSize);
delete []Buffer;
}
Код выше работает отлично, но выглядит ужасно; мы избавляемся от инкапсуляции std::string, использующей ее внутреннюю память с помощью метода std::string::c_str(), мы должны заботиться о распределениях и освобождениях от динамической памяти, играть с указателями и рассматривать все значения как символы без знака (см. следующую часть), вопрос в том, есть ли лучший способ сделать это?
Мои первые попытки решить вышеупомянутые проблемы с помощью std::copy не удовлетворяют меня вообще:
void send(const std::string &value)
{
const std::string::size_type Size(value.size());
const std::string::size_type TotalSize(sizeof(Size) + value.size());
std::vector<unsigned char> Buffer(TotalSize, 0);
std::copy(&Size, &Size + 1, Buffer.begin());
std::copy(value.begin(), value.end(), Buffer.begin() + sizeof(Size));
sendBuffer(Buffer.data(), TotalSize);
}
С помощью вышеописанного подхода управление памятью больше не является проблемой, std::vector берет на себя ответственность за распределение, хранение и окончательное удаление данных в конце области действия, но смешение вызовов std::copy с указателем арифметика и арифметика итераторов довольно раздражает, и в конце концов я игнорирую инкапсуляцию std::vector в вызове sendBuffer.
После предыдущих попыток я закодировал что-то с std::stringstream, но результаты были еще хуже, и теперь мне интересно, если:
- Существует способ сериализации объектов и значений безопасным способом, без нарушения инкапсуляции, без excesive или запутанной арифметики указателя/итератора и без управления динамической памятью или это просто невозможная цель? (да, я слышал о
boost::serialization, но пока мне не разрешено его интегрировать).
и
- Какое наилучшее использование
std::copyдля целей сериализации/десериализации? (если есть). - Обоснование
std::copyограничено для копирования контейнеров или массивов и использование его для необработанной памяти - плохой выбор?
alloc/free vs new/delete vs std::allocator
Другой большой темой является выделение памяти. AFAIK функции malloc/free не запрещены в область С++, хотя они из C. И операторы new/delete являются из области С++, и они не являются ANSI C.
- Я прав?
-
new/deleteможет использоваться в ANSI C?
Предполагая, что мне нужно реорганизовать все C-ароматизированные коды на С++-код, я избавляюсь от всего alloc/free распространенного arround некоторого унаследованного кода, и я обнаружил, что резервирование динамической памяти довольно запутывает, тип void не несет никакой информации о размере, из-за чего невозможно зарезервировать буфер данных с использованием void как type:
void *Buffer = new void[100]; // <-- How many bytes is each 'void'?
Из-за отсутствия указателей с чистым-сырым-двоичным-данным, является обычной практикой создания указателей на unsigned char. char, чтобы совместить количество элементов и их размер. И unsigned, чтобы избежать неожиданных конверсий без подписей во время копирования данных. Может быть, это обычная практика, но это беспорядок... unsigned char не int и float и my_awesome_serialization_struct, если я вынужден выбрать какой-то фиктивный указатель на двоичные данные, я предпочту void * вместо unsigned char *.
Поэтому, когда мне нужен динамический буфер для целей сериализации/десериализации, я не могу избежать материала unsigned char * для реорганизации в управление безопасным буфером типа; но когда я был рэг-рефакторинг всех пар alloc/free в пары new/delete, я читал о std::allocator.
std::allocator позволяет резервировать куски памяти безопасным способом, с первого взгляда я уверен, что это будет полезно, но нет больших различий между распределением с помощью std::allocator<int>::allocate или new int или так я думал, то же было для std::allocator<int>::deallocate и delete int.
И теперь, я потерял север относительно управления динамической памятью, поэтому я спрашиваю:
- Есть ли хорошая практика на С++, связанная с управлением динамической памятью для целей сериализации/десериализации, которая предоставляет управление безопасным типом?
- Можно ли избежать использования
const char *для буферов памяти сериализации/десериализации? - Какое обоснование
std::allocatorи каково его использование в области сериализации/десериализации? (если есть).
Спасибо за внимание!