C++ - 使用 std::sort 对结构向量进行排序导致读取访问冲突

Posted

技术标签:

【中文标题】C++ - 使用 std::sort 对结构向量进行排序导致读取访问冲突【英文标题】:C++ - Sorting vector of structs with std::sort results in read access violation 【发布时间】:2020-06-09 10:42:57 【问题描述】:

我对 std::sort 方法有疑问。在以下代码中,我使用 std::sort 方法对结构向量进行排序(= Highscore)。但是,当我运行此行时,xmemory 文件中会引发“读取访问冲突”异常。

以下是详细信息: 抛出异常:读取访问冲突。 _Pnext 为 0x217AE3EE9D8。发生

这是发生错误的方法。

void HighscoreManager::sortAndChangeRanks(bool deleteLast) 
    std::sort(_highscores.begin(), _highscores.end());
    if (deleteLast && _highscores.size() > MaxHighscores) 
        _highscores.pop_back();
    

    for (int i = 0; i < _highscores.size(); i++) 
        _highscores.at(i).rank = i + 1;
    

_highscores 定义为std::vector&lt;Highscore&gt; _highscores;,并在方法调用之前填充了来自文件的值。这工作得很好。当我在使用排序方法之前进行调试时,向量会被文件中的正确值填充。

这是 Highscore-struct 的实现:

struct Highscore 
    int rank;
    std::string name;
    int points;

    Highscore() 

    Highscore(int r, std::string n, int p) : rank(r), name(std::move(n)), points(p) 

    bool operator<(const Highscore& h1) const 
        return points < h1.points;
    
;

请帮助我或指出错误所在的方向,我没有想法。

编辑

由于在调用 std::sort 之前在 cmets 中询问了向量在哪里使用,因此这是从对象构造函数调用的方法,也是唯一一次在排序之前使用向量。这种从二进制文件中读取(写入工作类似)的方式是基于this。

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((char*)&size, sizeof(size));
        _highscores.resize(size);
        in.read((char*)&_highscores[0], _highscores.size() * sizeof(Highscore));        
     catch(const std::exception& e) 
        std::cout << e.what() << std::endl;
    

    in.close();
    sortAndChangeRanks(false);
    return in.good();

【问题讨论】:

嗯,循环本身并不是天生错误的。问题出在循环的 _highscores.at(i) 部分。 你需要这样改:for (int i = 0; i &lt; _highscores.size(); i++). 感谢您的帮助,您当然是对的!但这对我的问题没有帮助,因为 std::sort 的行在循环之前。 in.read((char*)&amp;_highscores[0], _highscores.size() * sizeof(Highscore)); - 这不是反序列化非平凡类的方式。你的班级有一个std::string,其中包含可变数量的chars。您的 sizeof(Highscore) 是静态的。看到问题了吗? This might help @Ted Lyngmo & Yksisarvinen 谢谢你的支持帮助我解决了这个问题!它现在可以正常工作了:) 【参考方案1】:

我不知道您的高分存储有哪些“优化”。这似乎只是白费力气。您没有存储数百万的高分。您可以将它们存储为文本。 “优化”在正常使用中无法衡量。如果您认为自己正在优化:显示测量值。否则你就是在自欺欺人,浪费时间。

最重要的是,您的代码已经足够复杂,以至于您遇到了一个需要很长时间才能调试的问题。这是一种学习体验,但严格来说,你因此浪费了更多时间。在大多数情况下,您的时间成本高于运行时间。

您所需要的只是可以在两分钟内完成的微不足道的文本流 I/O。如果您不完全了解发生了什么,则不建议使用二进制存储。就目前而言,如果您尝试阅读在具有不同字节序的机器上编写的高分,您的代码将会崩溃或更糟。现在你必须管理所有数字数据的字节顺序……祝你好运。

无论如何,这实际上是一种悲观化,因为你不断地重新分配临时字符串缓冲区。不需要该缓冲区。您应该调整字符串本身的大小并将数据放入其中。

std::string name(nLen);
in.read(&name[0], name.size());

【讨论】:

是的,你是对的,“优化”绝对不是一个合适的词选择,抱歉。我相应地更改了文本。感谢您提供有关 I/O 流的提示。我会调查这个【参考方案2】:

这是我正在使用的当前解决方案。这对我有用并解决了我的问题,即读取/写入 std::string 到二进制文件而不是排序方法(感谢问题中的 cmets!)。为了解决这个问题,我使用了this 的部分内容。

从文件中读取:

std::ifstream in(FileName, std::ios::in | std::ios::binary);
    if(!in)    
        return false;
    

    try    
        std::vector<Highscore>::size_type size = 0;
        in.read((char*)&size, sizeof(size));
        for(int i = 0; i < size; i++) 
            int r, p;
            size_t nLen;
            in.read((char*)&r, sizeof(int));
            in.read((char*)&p, sizeof(int));
            in.read((char*)&nLen, sizeof(size_t));

            char* temp = new char[nLen + 1];
            in.read(temp, nLen);
            temp[nLen] = '\0';
            std::string name = temp;
            delete[] temp;

            _highscores.emplace_back(r, name, p);
        
     catch(const std::exception& e) 
        std::cout << e.what() << std::endl;
    

    in.close();
    sortAndChangeRanks(false);
    return in.good();

写入文件:

bool HighscoreManager::saveToFile() 
    std::ofstream out(FileName, std::ios::out | std::ios::binary);
    if(!out) 
        return false;
    

    std::vector<Highscore>::size_type size = _highscores.size();
    try 
        out.write((char*)&size, sizeof(size));
        for(int i = 0; i < size; i++) 
            out.write((char*)&_highscores.at(i).rank, sizeof(int));
            out.write((char*)&_highscores.at(i).points, sizeof(int));
            size_t nameLen = _highscores.at(i).name.size();
            out.write((char*)&nameLen, sizeof(size_t));
            out.write((char*)_highscores.at(i).name.c_str(), nameLen);
        
     catch (const std::exception& e) 
        std::cout << e.what() << std::endl;
    
    out.close();

    return out.good();

感谢大家的帮助!

【讨论】:

以上是关于C++ - 使用 std::sort 对结构向量进行排序导致读取访问冲突的主要内容,如果未能解决你的问题,请参考以下文章

使用 std::sort() 对对象向量进行排序

std::sort 对包含 Objects* 指针的 Objects 向量进行排序

C++排序方法

使用 std::sort 对具有特定标准的二维向量进行排序

如何使用 std::sort() 对指向值的向量进行排序?

如何使用单独类的属性对向量进行 std::sort [关闭]