Я долгое время работал с аппаратными API, и почти все API, с которыми я работал, имели C-интерфейс. Итак, много раз я работал с голым new
s, небезопасная буферизация и многие функции C, обернутые кодом C ++. В конце концов, граница между чистым кодом C и чистым кодом C ++ была запутана в моем сознании (и я не знаю, полезно ли вообще разъяснять эту границу).
Теперь, из-за некоторых новых требований к стилю кодирования, мне нужно реорганизовать весь код, который предположительно небезопасен, в более безопасный, написанный на C ++ (при условии, что код C ++ будет более безопасным), конечная цель — повысить безопасность кода с помощью инструменты, которые дает C ++.
Итак, чтобы избавиться от всей моей путаницы, я прошу помощи по нескольким темам C / C ++.
memcpy
против std::copy
насколько мне известно memcpy
это функция, которая лежит в библиотеках C, так что это не C ++ ish; с другой стороны std::copy
это функция в STL, так что это чистый C ++.
std::copy
позвоню std::memcpy
(в cstring
заголовок), если данные тривиально копируемы.memcpy
призывает в std::copy
вызовы сделали бы код более «чистым C ++»?Чтобы справиться с новыми требованиями стиля кода, я решил продолжить 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
Но результаты были еще хуже, и теперь мне интересно, если:
boost::serialization
, но пока я не могу интегрировать его).А также:
std::copy
для сериализации / десериализации? (если есть).std::copy
Объяснение ограничено для копирования контейнеров или массивов, и использование его для сырой памяти является плохим выбором?alloc
/free
против new
/delete
против std::allocator
Другая большая тема — распределение памяти. AFAIK malloc
/free
функции не запрещены в области видимости C ++, хотя они из C. И new
/delete
операторы из области C ++, и они не ANSI C.
new
/delete
можно использовать в ANSI C?Предполагая, что мне нужно реорганизовать весь код на основе C в код C ++, я избавляюсь от всех alloc
/free
Мы распространили несколько устаревшего кода, и я обнаружил, что резервирование динамической памяти довольно запутанно, тип void не несет никакой информации о размере, из-за чего невозможно зарезервировать буфер данных, используя void в качестве типа:
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
и как он используется в области сериализации / десериализации? (если есть).Спасибо за внимание!
Мой опыт показывает, что безопасность типов в C ++ означает не только то, что компилятор жалуется на несоответствия типов. Это скорее означает, что вам вообще не нужно заботиться о расположении памяти в ваших данных. На самом деле, стандарт C ++ предъявляет очень мало требований к разметке памяти определенных типов данных.
Ваша сериализация основана на прямом доступе к памяти, поэтому, боюсь, не будет простого «чистого» решения C ++ и, в частности, не будет общего решения, независимого от компилятора / платформы.
Других решений пока нет …