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

时间:2013-11-30 02:04:41

标签: c++ sorting memory-leaks bad-alloc mingw-w64

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

但是,这个测试用例甚至更奇怪: 我正在读一个带有几百行的146KB文件,这次我在将数据读入一个向量时遇到异常,这在以前成功读取70MB是完全荒谬的。

我怀疑内存泄漏,但我的机器有8GB内存,使用64位Windows 8。 我正在使用CodeBlocks和MinGW 64位增强版。 任何帮助,将不胜感激。 下面是一段代码,其中抛出了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;
        }
     }; 
    
  3. 然后,我正在使用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) 哦,另外要指出的是,由于我的MinGW是64位,我不能使用默认的32位gdb调试器。 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.不可比性的传递性

    更多信息:This questionThis Wiki article
    实际上,我没有遵循第一个(反射性),也就是说,如果两个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
    }
    

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

1 个答案:

答案 0 :(得分:1)

此:

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

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

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

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

当然,我们绝不应该尝试delete这些价值观。