如何在不含向量数据的情况下存储包含std::vector的结构体?
首先得说,你现在直接二进制读写整个结构体的做法本质上就有问题——std::vector属于非POD(Plain Old Data)类型,它内部存储的是指向堆内存的指针、当前元素数量、容量这些元数据,你把这些值直接序列化到文件里,加载的时候这些指针指向的内存早就被释放了,自然会触发崩溃,哪怕你清空vector再存也可能有坑(因为clear()只会把元素数量设为0,容量和内部指针可能还保留着)。
给你几个靠谱的解决方案,按简单到规范的顺序来:
方案一:序列化前彻底重置vector,加载后直接用
你之前试了清空但还是崩溃,那是因为clear()没彻底重置vector的内部状态。试试用swap来彻底清空vector:
// 序列化前,对每个vector执行这个操作 std::vector<YourType>().swap(yourVec);
这个操作会把vector的容量、大小都重置为0,内部的指针也会变成安全的空状态(不同实现可能是nullptr或者特殊的空标记,但肯定是合法的)。这时候再序列化整个结构体,加载后vector就是完全干净的空状态,不会有无效指针的问题,后续resize、push_back都能正常工作。
方案二:手动序列化需要的成员,跳过vector
如果你的结构体里只有少数几个vector是不需要保存的,那不如直接手动序列化那些需要保留的字段,完全跳过vector。比如你的结构体是这样的:
struct ProgramSettings { int windowSizeX; int windowSizeY; bool fullscreen; std::vector<int> tempCache; // 不需要保存的vector };
那保存函数可以改成这样:
void saveSettings(std::string filename) { std::ofstream file(filename, std::ios::binary); if (file.is_open()) { // 只写需要保存的成员 file.write(reinterpret_cast<const char*>(&Settings.windowSizeX), sizeof(Settings.windowSizeX)); file.write(reinterpret_cast<const char*>(&Settings.windowSizeY), sizeof(Settings.windowSizeY)); file.write(reinterpret_cast<const char*>(&Settings.fullscreen), sizeof(Settings.fullscreen)); file.close(); } else std::cout << "Error saving Settings.dat" << std::endl; }
加载的时候反过来,只读取这些字段,vector会保持默认的空状态,完全不会有问题。这个方法虽然要多写几行代码,但最安全,也完全符合你“不存vector数据”的需求。
方案三:用专业的序列化库(长期维护推荐)
如果你的结构体比较复杂,以后可能还要加字段,那不如直接用成熟的序列化库,比如Boost.Serialization或者nlohmann/json(文本格式,易读易调试)。这些库能正确处理std::vector,而且你可以很方便地配置哪些字段需要序列化,哪些跳过。比如用nlohmann/json的话,你可以给结构体写一个自定义的序列化规则,直接忽略vector字段。
最后必须提醒你:那个VectorHack绝对不能用!std::vector的内部成员布局是C++标准未定义的,不同编译器、不同版本的编译器,vector的成员数量、顺序都可能不一样。你现在在你的机器上改那几个字段能工作,换个环境(比如换MSVC到GCC,或者升级编译器版本)肯定会直接崩溃,属于典型的未定义行为,完全不可靠。
备注:内容来源于stack exchange,提问作者Paul Aner

