C++ создает исключение std::bad_alloc для очень маленького std::vector с использованием std::sort

Я работаю над проектом на C++, который имеет дело с данными, разделенными запятыми (CSV). Я читаю данные из файла.csv в вектор объектов CsvRow.
Итак, сегодня я столкнулся с действительно странными исключениями std::bad_alloc, возникающими в гораздо более странных ситуациях. А именно, первым тестовым примером, в котором мне удалось получить немного больше времени, пока я не сгенерировал исключение, было чтение целого файла csv в вектор. Файл состоит из 500000 строк, а его размер составляет около 70 МБ. Файл был прочитан в память, как талисман, но затем, через несколько секунд после процедуры сортировки, генерируется std::bad_alloc. Он использовал примерно 67 МБ оперативной памяти. Примечание: я использую грузоподъемность Boost, чтобы уменьшить потребление памяти.

НО, этот тестовый случай был еще более странным: я читаю файл размером 146 КБ с несколькими сотнями строк, и на этот раз я получил исключение при чтении данных в вектор, что совершенно нелепо, если ранее было успешно прочитано 70 МБ.

Я подозреваю утечку памяти, но моя машина имеет 8 ГБ ОЗУ, используя 64-разрядную Windows 8. Я использую CodeBlocks и 64-разрядный дистрибутив MinGW 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.

Редактировать:
Может ли boost::flyweight<std::string> который я использую в классе CsvRow, вызывает проблему?

В дополнение к этому, вот часть 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 a а также CsvRow 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 ответ

Решение

Это:

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

Похоже, вы пытаетесь добавить один созданный по умолчанию элемент к вектору. Есть более простой способ:

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

Помимо того, что легче набирать текст и быть более общим, оно также намного правильнее: мы не должны newЧто-то здесь, просто создадим одно значение в конце, и мы должны позволить вектору создать его, а не копировать, что может быть расточительным.

И, конечно, мы никогда не должны пытаться delete эти значения.

Другие вопросы по тегам