我使用硬件 API 已经有很长时间了,而且我一直在使用的几乎所有 API 都有一个 C 接口。所以,很多时候我都在使用裸new
s、不安全的缓冲和许多用 C++ 代码包装的 C 函数。最后,C 纯代码和 C++ 纯代码之间的边界在我的脑海中被搞乱了(我不知道澄清这个边界是否有用)。
现在,由于一些新的编码风格要求,我需要将所有怀疑不安全的代码重构为用 C++ 编写的更安全的代码(假设 C++ 代码会更安全),最终目标是使用C++ 带来的工具。
因此,为了摆脱我所有的困惑,我正在寻求有关 C/C++ 几个主题的帮助。
memcpy
对比std::copy
AFAIKmemcpy
是 C 库中的一个函数,所以它不是 C++ 语言;另一方面std::copy
是 STL 中的一个函数,所以它是纯 C++。
- 但是,这是真的吗?毕竟,如果数据可以轻松复制,
std::copy
则将调用std::memcpy
(到cstring
标题中)。 memcpy
将所有调用重构为std::copy
调用会使代码更“纯 C++”?
memcpy
毕竟,为了处理新的代码风格要求,我决定继续重构,关于memcpy
and有一些问题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
s 编写了一些代码,但结果更糟,现在,我想知道是否:
- 有一种方法可以安全地序列化对象和值,而不会破坏封装,不会过度或混淆指针/迭代器算术,也没有动态内存管理,或者这只是一个不可能的目标?(是的,我听说过
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
ornew int
或左右分配并没有太大区别,对于std::allocator<int>::deallocate
and 也是如此delete int
。
现在,我对动态内存管理失去了兴趣,这就是我要问的原因:
- 有一个很好的 C++ 实践涉及用于序列化/反序列化目的的动态内存管理,它授予类型安全管理?
- 是否可以避免使用
const char *
用于序列化/反序列化的内存缓冲区? - 序列化/反序列化范围的基本原理
std::allocator
和用途是什么?(如果有的话)。
感谢您的关注!