直接用std::ofstream写结构体字段会出问题,因结构体内存布局含指针、虚表、对齐填充、字节序差异及STL成员(如std::string)的堆外数据,导致二进制dump仅保存地址而非实际内容,反序列化后出现悬空指针、字段错乱或崩溃;且纯数据流无法携带元信息,必须显式声明序列化映射关系。

为什么直接用 std::ofstream 写结构体字段会出问题
因为 C++ 结构体/类的内存布局不等于可读文本格式,直接二进制 dump 或逐字段拼接字符串,既不可靠也不可维护。比如 std::string 在内存里是指针+长度,std::vector 是动态分配的,还有虚函数表、对齐填充、字节序等干扰项。更关键的是——引擎里属性常带元信息(如“是否编辑器可见”“默认值”“范围限制”),纯数据流无法携带这些。
怎么让 YAML::Node 或 nlohmann::json 知道该序列化哪些字段
必须显式声明映射关系,不能靠反射(C++17 原生无运行时反射)。主流做法是为每个需序列化的类型重载 operator(YAML)或 to_json/from_json(JSON):
- YAML-CPP:定义
template struct YAML::convert{ static Node encode(const MyComponent& rhs); static bool decode(const Node& node, MyComponent& rhs); }; - JSON for Modern C++:在命名空间内声明
void to_json(nlohmann::json& j, const MyComponent& c); void from_json(const nlohmann::json& j, MyComponent& c); - 别漏掉 const 限定符和引用传递,否则编译失败或性能损耗
- 嵌套对象要递归调用各自序列化函数,不是手动展开字段
如何避免序列化时丢掉引擎特有属性(如资源路径、对象引用)
引擎中常见“软引用”(例如 AssetPath 类型字段)或“对象 ID”(如 EntityID),它们在磁盘上应存为字符串或整数,而非原始指针或句柄。序列化函数里必须做转换:
-
AssetPath→node["asset"] = path.ToString(); -
EntityID→j["entity_id"] = id.Value();(假设Value()返回 uint32_t) - 反序列化时反过来:用
AssetPath::FromStr(node["asset"].as<:string>()) - 绝对不要序列化
std::shared_ptr本身,只序列化它指向对象的 ID 或路径;否则加载时无法重建引用图
写入磁盘前要不要先生成临时 YAML::Node 或 nlohmann::json 对象
要。直接边遍历边写文件,一旦中途出错(比如字段类型不匹配、内存不足),文件就损坏且不可恢复。正确流程是:
- 构建完整
YAML::Node或nlohmann::json树 - 调用
YAML::Dump(node)或j.dump(2)得到字符串 - 再用
std::ofstream一次性写入磁盘 - 检查
ofstream::fail(),失败时保留原文件(不覆盖),并返回错误码 - JSON 的
dump(2)保证缩进,YAML 默认已可读;但别依赖dump(-1)(无缩进)——调试时几乎无法人工核对
实际最难的部分不在语法,而在保持序列化/反序列化逻辑与类定义同步:字段增删改后,三个地方必须同时更新——类定义、序列化函数、反序列化函数。漏掉任一环节,加载就会静默失败或读错值。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











