不能直接 memcpy 对象,因其会原样拷贝指针、虚表指针及 std::string/vector 等内部堆成员,导致地址失效;且 padding、abi、字节序差异使跨平台/存盘后必然出错,必须显式序列化。

为什么不能直接 memcpy 对象?
因为 memcpy 会把对象内存原样拷贝,但 C++ 对象里可能藏了指针、虚函数表指针、std::string 或 std::vector 这类内部堆分配的成员——它们的地址一复制就失效。更糟的是,不同编译器/平台对 padding、ABI、字节序的处理不一致,跨进程或存盘后几乎必然读错。
所以必须显式控制每个字段的读写顺序和格式,哪怕只是二进制,也得自己走一遍“序列化逻辑”。
怎么写一个安全的 serialize() 成员函数?
核心是:只操作 POD 字段 + 显式处理非 POD 成员。比如有个 Person 类:
struct Person {
int id;
char name[32];
std::vector<int> scores;
<pre class='brush:php;toolbar:false;'>void serialize(std::ostream& out) const {
out.write(reinterpret_cast<const char*>(&id), sizeof(id));
out.write(name, sizeof(name));
size_t sz = scores.size();
out.write(reinterpret_cast<const char*>(&sz), sizeof(sz));
if (!scores.empty()) {
out.write(reinterpret_cast<const char*>(scores.data()), sz * sizeof(int));
}
}};
立即学习“C++免费学习笔记(深入)”;
要点:
-
serialize()只负责输出,不管理流状态(比如是否打开、是否失败)——调用方负责检查out.good() - 对
std::vector等容器,先写长度再写数据,避免反序列化时无法预分配 - 所有
write()前都用reinterpret_cast<const char></const>转换,别漏掉const - 如果字段含
std::string,要先写长度再写 c_str(),且注意 null terminator 不自动包含
反序列化时最容易崩在哪几个地方?
崩溃常发生在没校验输入就直接 reinterpret_cast 回结构体,或者忘了跳过 padding 字段。常见错误现象:segmentation fault、std::bad_alloc(vector 分配超大 size)、读出的 id 是乱码数字。
安全做法:
- 反序列化函数必须返回
bool表示成功,而不是 void - 每次
read()后立刻检查in.gcount() == sizeof(T),不能只靠good() - 对
std::vector,先读size_t sz,再验证sz (防恶意构造超大值导致 OOM) - 如果类有继承关系,基类字段必须在派生类
serialize()中显式调用,否则会被跳过
要不要用 std::bit_cast 或 memcpy 替代 reinterpret_cast?
不要。C++20 的 std::bit_cast 要求源和目标类型 trivially copyable 且大小相等,但它不解决字段布局问题;而 memcpy 比 reinterpret_cast 多一层安全检查(比如编译器能诊断重叠),但依然无法绕过虚函数表、指针、非 POD 成员的陷阱。
真正关键的是:序列化协议本身是否可移植。如果你只在同构系统(同一编译器、同一 ABI、小端)内用,手写二进制序列化够用;一旦涉及网络传输或长期存储,就得加版本号字段、考虑字节序转换(用 htons/ntohl),否则几年后连自己都读不出来。








