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:
Чтение данных из 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++; }
Сортировка вектора с использованием внутренней структуры, хранящейся в моем классе
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
эти значения.