C ++, удалить дубликаты - PullRequest
       21

C ++, удалить дубликаты

3 голосов
/ 08 апреля 2011

Что не так в этом коде?Я пытаюсь удалить дубликаты элементов из определенного пользователем контейнера.

template <typename Item>
struct TList
{
    typedef std::vector <Item> Type;
};

template <typename Item>
class GenericContainer
{
    protected:
            typename TList <Item>::Type items;
};

Существует метод удаления дубликатов элементов в контейнере, специализированный для элемента * (т. Е. Элементы выделяются динамически):

template <typename Item>
template <typename Sort, typename Equal>
void Container <Item * >::remove ( typename TList <Item *>::Type ::iterator it_begin, typename TList <Item *>::Type ::iterator it_end,     Sort sort, Equal equal )
{           //Sort items, ok 
    std::sort ( it_begin, it_end, sort );

            //Apply unique, OK
    typename TList <Item *>::Type ::iterator i_new_end = std::unique ( it_begin, it_end, equal );

            //Delete items after new end of container, OK
    for (typename TList <Item *>::Type ::iterator i_item = i_new_end ; i_item != it_end; i_item ++)
    {
        delete *i_item; //call destructor
    }

            //Erase duplicate items
    this->items.erase ( i_new_end, it_end );

            //Another sort, Exception: Acces in free memory
    std::sort ( it_begin, i_new_end, sort );

);

Не могу найти проблему в строке

            //Another sort, Exception: Acces in free memory
    std::sort ( it_begin, i_new_end, sort );

или в строках раньше ...

Error log:
Access in freed memory in process: sw.exe(line 796)  

Спасибо за ваш совет.

Обновлен вопрос:

Я перевожу код другим компилятором (MSVS 2010) и проверял элементы вектора.Вот результаты:

Входной набор данных: 628 элементов.

A) std::sort ( it_begin, it_end, sort ); 

628 элементов

B) typename TList <Item *>::Type ::iterator i_new_end = std::unique ( it_begin, it_end, equal ); 

612 уникальных элементов

C) for (typename TList <Item *>::Type ::iterator i_item = i_new_end ; i_item != it_end; i_item ++)
{
    delete *i_item; //call destructor
}

Кмои неожиданные предметы, так как элемент [595] был удален (почему не элемент [613] ???).Я не понимаю такого странного поведения ...

Ответы [ 4 ]

2 голосов
/ 08 апреля 2011

Моя ставка заключается в том, что не только некоторые значения появляются более одного раза, некоторые отдельные объекты появляются более одного раза в последовательности.

Когда вы delete все дублируете значения, вы уничтожаете некоторые объекты, которые остаются в последовательности.

Второй вид (который не нужен, так как unique не переставляет вещи, так как удаляет дубликаты) обращается к каждому объекту, поэтому он немедленно наступает на те, которые были просто delete d.

Одним из возможных решений является sort указатели в обоих диапазонах, следующие из unique. Используйте set_difference( i_new_end, it_end, i_begin, i_new_end, i_sequence_inserter ), чтобы найти объекты, которые действительно должны быть освобождены - при условии, что они больше нигде не используются.

Или просто используйте умные указатели или вообще не указывайте: v).


Edit:

См. Мой комментарий - лучшее решение, вероятно, полностью исключит использование указателей.

В любом случае, вот пример решения set_difference, в котором используется пользовательский итератор вместо средства вставки последовательности.

template <typename Item>
template <typename Sort, typename Equal>
void Container <Item * >::remove ( typename TList <Item *>::Type ::iterator it_begin, typename TList <Item *>::Type ::iterator it_end,     Sort sort, Equal equal )
{           //Sort items, ok 
    std::sort ( it_begin, it_end, sort );

            //Apply unique, OK
    typename TList <Item *>::Type ::iterator i_new_end = std::unique ( it_begin, it_end, equal );

    // Now sort the sub-ranges on pointer values to identify duplicate pointers
    std::sort( it_begin, i_new_end );
    std::sort( i_new_end, it_end );

    // delete all pointers that appear only in the set of duplicate values to be erased
    struct deleter {
        deleter &operator *() { return *this; } // assignment to target is assgn. to iter
        deleter &operator =( Item *rhs ) { delete rhs; return *this; }
        deleter &operator ++() { return *this; } // increment is no-op
        deleter &operator ++(int) { return *this; } // increment is no-op
    };
    std::set_difference( i_new_end, it_end, it_begin, i_new_end, deleter() );

            //Erase duplicate items
    this->items.erase ( i_new_end, it_end );

            //Another sort, Exception: Acces in free memory
    std::sort ( it_begin, i_new_end, sort );

);
0 голосов
/ 08 апреля 2011

После небольшого тестирования, я думаю, i_new_end становится недействительным при вызове .erase().

Согласно документации , оно должно быть действительным - только указатели после i_new_end должны быть признаны недействительными. Но VisualC ++ явно не следует этому. Это почти правильно, i_new_end и end () оба указывают на одно и то же место в памяти, но _Mycont сбрасывается в ноль для i_new_end, что делает указатель недействительным.

То есть, даже если они указывают на один и тот же адрес, это не получается:

  if (i_new_end._Myptr == end()._Myptr)  // yes, they're the same
  if (i_new_end == end())  // boom.  exception

Итак, попробуйте это:

// remember next-to-last item
typename TList <Item *>::Type ::iterator nextlast = i_new_end - 1;

//Erase duplicate items
this->items.erase ( i_new_end, it_end );

// reset i_new_end
i_new_end = nextlast + 1;

// Now this line should not throw
std::sort ( it_begin, i_new_end, sort );

Также имейте в виду проблемы с памятью, упомянутые Potatoswatter.

0 голосов
/ 08 апреля 2011

Мне кажется, что переданные итераторы на самом деле не из this->items, а из какого-то другого контейнера.

Другая возможность состоит в том, что delete *i_item вызывает проблему и отображается только впоследняя строка в виде отложенной реакции.

РЕДАКТИРОВАТЬ: Еще один вариант, который встречается гораздо чаще, чем вы ожидаете, заключается в том, что ваш предикат sort не подчиняется строгому слабому порядку.Не могли бы вы показать код для этого?

0 голосов
/ 08 апреля 2011

Похоже, что итераторы, использованные в последнем вызове std::sort, могут быть недействительными.Вероятно it_begin.Можете ли вы попробовать использовать this->items.begin() вместо?

...