Я работаю над проектом на C ++, который имеет дело с данными, разделенными запятыми (CSV). Что я делаю, так это чтение данных из файла .csv в вектор объектов CsvRow.
Итак, сегодня я столкнулся с действительно странными исключениями std :: bad_alloc, возникающими в гораздо более странных ситуациях. А именно, первым тестовым примером, в котором мне удалось получить немного больше времени, пока я не сгенерировал исключение, было чтение целого файла csv в вектор. Файл состоит из 500 000 строк, а его размер составляет около 70 МБ. Файл считывался в память, как талисман, но затем, через несколько секунд после процедуры сортировки, выдается std :: bad_alloc. Он использовал примерно 67 МБ оперативной памяти
Примечание: я использую маховики boost для уменьшения потребления памяти.
НО, этот тест был еще более странным:
Я читаю файл размером 146 КБ с несколькими сотнями строк, и на этот раз я получил исключение при чтении данных в вектор, что совершенно нелепо, если ранее было успешно прочитано 70 МБ.
Я подозреваю утечку памяти, но моя машина имеет 8 ГБ оперативной памяти, используя 64-разрядную версию Windows 8.
Я использую CodeBlocks и дистрибутив с 64-битным бустом MinGW.
Любая помощь будет оценена.
Вот фрагмент кода, в который выбрасывается 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. Транзитивность несопоставимости
Больше информации на:Этот вопрос а также Эта статья в вики
На самом деле, я не следовал за первым (нерефлексивность), то есть, если оба 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
}
Спасибо всем, кто пытался помочь.
Запомните это решение, если у вас возникла подобная проблема. Это довольно сложно.
Это:
distinct_values_.push_back( *new __gnu_cxx::hash_set<std::string,
std::hash<std::string> >() );
Похоже, вы пытаетесь добавить один созданный по умолчанию элемент к вектору. Есть более простой способ:
distinct_values_.resize(distinct_values_.size() + 1);
Помимо того, что легче набирать текст и быть более общим, оно также намного правильнее: мы не должны new
что-то здесь, просто создаем одно значение в конце, и мы должны позволить вектору создать его, а не копировать в него, что может быть расточительным.
И, конечно, мы никогда не должны пытаться delete
эти значения.
Других решений пока нет …