C++使用std::sort为非常小的std::vector抛出std::bad_alloc异常

C++ throwing a std::bad_alloc exception for very small std::vector using std::sort

本文关键字:std bad 抛出 alloc vector 异常 sort 使用 非常 C++      更新时间:2023-10-16

我正在C++中进行一个项目,该项目处理逗号分隔数据(CSV)。我所做的是将.csv文件中的数据读取到CsvRow对象的向量中
所以,今天我遇到了一个非常奇怪的std::bad_alloc异常,它在更奇怪的情况下被抛出。也就是说,我设法获得更多时间直到抛出异常的第一个测试用例是将整个csv文件读取到向量中。该文件由500000行组成,大小约为70MB。该文件像符咒一样被读入内存,但在排序过程中几秒钟后,std::bad_alloc被抛出。它使用了大约67MB的RAM注意:我使用boost的轻量级是为了减少内存消耗。

但是,这个测试用例更奇怪:我正在读取一个146KB的文件,其中有几百行,这次我在将数据读取到向量中时遇到了异常,之前成功读取了70MB,这真是太荒谬了。

我怀疑是内存泄漏,但我的机器有8GB的RAM,使用64位Windows8。我使用的是CodeBlocks和MinGW 64位boost发行版。如有任何帮助,我们将不胜感激。下面是一段代码,其中抛出std::bad_alloc:

  1. 从csv文件读取数据

    std::ifstream file(file_name_);
    int k=0;
    for (CsvIterator it(file); it != CsvIterator(); ++it) {
        if(columns_ == 0) {
            columns_ = (*it).size();
            for (unsigned int i=0; i<columns_; i++) {
                 distinct_values_.push_back(*new __gnu_cxx::hash_set<std::string,                         
                                            std::hash<std::string> >());
            }
        }
        for (unsigned int i=0; i<columns_; i++) {
            distinct_values_[i].insert((*it)[i]);
        }
        all_rows_[k]=(*it);
        k++;
    }
    
  2. 使用存储在我的类中的内部结构对向量进行排序

    struct SortRowsStruct
    {
        CsvSorter* r;
        SortRowsStruct(CsvSorter* rr) : r(rr) { };
        bool operator() (CsvRow a, CsvRow b)
        {
            for (unsigned int i=0; i<a.size(); i++) {
                if(a[r->sorting_order_[i]] != b[r->sorting_order_[i]]) {
                    int dir = r->sorting_direction_[i];
                    switch(dir) {
                        case 0:
                            return (a[r->sorting_order_[i]] < b[r->sorting_order_[i]]);
                            break;
                        case 1:
                            return !(a[r->sorting_order_[i]] < b[r-    >sorting_order_[i]]);
                            break;
                        case 2:
                            return true;
                            break;
                        default:
                            return true;
                    }    
                }
            }
            return true;
        }
     }; 
    

然后,我使用std::sort()对CsvRows 的向量进行排序

SortRowsStruct s(this);
std::sort(all_rows_.begin(), all_rows_.end(), s);

这一行看起来真的很可疑,但我想不出一种更简单的方法来初始化这些哈希集

distinct_values_.push_back( *new __gnu_cxx::hash_set<std::string,                                     
                             std::hash<std::string> >() ); 

删除析构函数中的哈希集会使程序崩溃(SIGSEGV)哦,还有一点需要指出,我不能使用默认的32位gdb调试器,因为我的MinGW是64位的。32位gdb已被窃听,无法与MinGW 64配合使用。

编辑:
我在CsvRow类中使用的
boost::flyweight<std::string>会导致问题吗

除此之外,这里还有CsvRow类的一部分:

private:
    std::vector<boost::flyweights::flyweight<std::string> > row_data_;

以及CsvRow类上过载的[]运算符:

std::string const& CsvRow::operator[](std::size_t index) const
{
    boost::flyweights::flyweight<std::string> fly = row_data_[index];
    return fly.get();
}

提前感谢

编辑-已解决:所以,这个问题解决了我的问题,尽管我甚至没有想过。我们传递给std::sort()的每个自定义比较器都必须是严格的弱排序,即:
1.不灵活
2.不对称
3.及物性
4.不可比性的传递性

更多信息,请访问:此问题和此Wiki文章
事实上,我没有遵循第一个(不可伸缩性),也就是说,如果两个CsvRow对象都相等,它不应该"比较"它们并返回true,就好像它们没问题一样,而是返回false。当CsvRow aCsvRow b都相等时,我只更改了默认返回值,就解决了整个问题。

bool operator() (CsvRow a, CsvRow b)
{
    for (unsigned int i=0; i<a.size(); i++) {
        if(a[r->sorting_order_[i]] != b[r->sorting_order_[i]]) {
            ...
            ...
        }
    }
    return false;  //this line does not violate the irreflexivity rule
    //return true;   //but this one does
}

感谢所有试图提供帮助的人。记住这个解决方案,以防遇到类似的问题。这很棘手。

这:

distinct_values_.push_back( *new __gnu_cxx::hash_set<std::string,                                     
                            std::hash<std::string> >() );

看起来您正试图将一个默认构造的元素添加到向量中。有一种更简单的方法:

distinct_values_.resize(distinct_values_.size() + 1);

除了更容易键入,更通用之外,它也更正确:我们不应该在这里使用new,只在最后创建一个值,我们应该让向量构造它,而不是复制它,这可能是浪费。

当然,我们永远不应该尝试delete这些值。