使用std::sort排序结构体vector触发读取访问违例求助
问题排查与解决方案
嘿,我一眼就揪出了导致你崩溃的元凶——你对包含std::string的结构体的二进制读写方式完全错误,这才让排序时触发了读取访问违例!
核心问题分析
你的Highscore结构体里包含std::string name,而std::string是一个非平凡类型:它内部并不是直接存储字符串内容,而是保存了指向堆内存的指针、字符串长度等元数据。当你用下面这段代码直接读写整个结构体时:
in.read((char*)&_highscores[0], _highscores.size() * sizeof(Highscore));
你实际上只是把std::string里的指针地址、长度这些值写入了文件。当你从文件读回来时,这些指针指向的内存早就被释放(或者是完全无效的垃圾地址)了。
虽然你重载的operator<只比较points字段,但std::sort在排序过程中会频繁移动、拷贝容器里的元素——这个过程会触发std::string的析构、拷贝构造或移动构造,而这些操作都会访问到无效的指针,直接导致内存访问违例(也就是你看到的_Pnext was 0x217AE3EE9D8错误)。
解决方案:正确序列化/反序列化结构体
你需要手动逐个字段处理Highscore的读写,不能直接整块内存拷贝。下面是修复后的代码示例:
1. 修复读取逻辑(loadFromFile)
bool HighscoreManager::loadFromFile() { std::ifstream in(FileName, std::ios::in | std::ios::binary); if(!in) { return false; } try { std::vector<Highscore>::size_type size = 0; // 先读取元素数量 in.read(reinterpret_cast<char*>(&size), sizeof(size)); if (!in) { return false; } _highscores.clear(); _highscores.reserve(size); for (std::vector<Highscore>::size_type i = 0; i < size; ++i) { int rank = 0; int points = 0; // 读取rank in.read(reinterpret_cast<char*>(&rank), sizeof(rank)); // 读取字符串长度 std::string::size_type name_len = 0; in.read(reinterpret_cast<char*>(&name_len), sizeof(name_len)); // 读取字符串内容 std::string name(name_len, '\0'); in.read(&name[0], name_len); // 读取points in.read(reinterpret_cast<char*>(&points), sizeof(points)); if (!in) { _highscores.clear(); return false; } _highscores.emplace_back(rank, std::move(name), points); } } catch(const std::exception& e) { std::cout << e.what() << std::endl; _highscores.clear(); return false; } sortAndChangeRanks(false); return true; }
2. 配套的写入逻辑(补上你需要的saveToFile)
bool HighscoreManager::saveToFile() { std::ofstream out(FileName, std::ios::out | std::ios::binary | std::ios::trunc); if(!out) { return false; } try { std::vector<Highscore>::size_type size = _highscores.size(); // 写入元素数量 out.write(reinterpret_cast<const char*>(&size), sizeof(size)); for (const auto& hs : _highscores) { // 写入rank out.write(reinterpret_cast<const char*>(&hs.rank), sizeof(hs.rank)); // 写入字符串长度 std::string::size_type name_len = hs.name.size(); out.write(reinterpret_cast<const char*>(&name_len), sizeof(name_len)); // 写入字符串内容 out.write(hs.name.data(), name_len); // 写入points out.write(reinterpret_cast<const char*>(&hs.points), sizeof(hs.points)); } } catch(const std::exception& e) { std::cout << e.what() << std::endl; return false; } return out.good(); }
额外的小建议
- 检查你的
operator<逻辑:你现在是按points升序排序(分数低的在前),如果你的排行榜需要分数高的排前面,应该改成:bool operator<(const Highscore& h1) const { return points > h1.points; // 降序排序 } - 文件操作时尽量依赖RAII(比如
std::ifstream/std::ofstream的析构自动关闭文件),不要手动调用close(),除非你需要立即检查关闭结果。
内容的提问来源于stack exchange,提问作者Janfiderheld
相关产品推荐
相关产品推荐

