C ++,删除重复的项目

时间:2011-04-08 17:17:00

标签: c++ duplicates

此代码有什么问题?我试图从用户定义的容器中删除重复的元素。

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

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

有一种方法可以删除专用于Item *的容器中的重复元素(即项目是动态分配的):

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 个答案:

答案 0 :(得分:2)

我敢打赌,不仅一些值出现不止一次,一些单个对象在序列中出现不止一次。

当你delete所有重复的值时,你会破坏序列中剩余的一些对象。

第二种排序(这是不必要的,因为unique不重新排列事物,因为它删除了重复项)访问每个对象,所以它立即踩到那些只是delete d。

一种可能的解决方案是sortunique产生的两个范围内的指针set_difference( i_new_end, it_end, i_begin, i_new_end, i_sequence_inserter )。使用set_difference查找实际需要释放的对象 - 假设它们还没有被其他地方使用。

或者,只使用智能指针,或者根本不使用指针:v)。


编辑:

请参阅我的评论 - 最佳解决方案可能完全消除指针的使用。

无论如何,这是一个示例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 ); ); 解决方案,这个解决方案使用自定义迭代器而不是序列插入器。

{{1}}

答案 1 :(得分:0)

听起来在std::sort的最后一次调用中使用的迭代器可能无效。可能是it_begin。您可以尝试使用this->items.begin()吗?

答案 2 :(得分:0)

在我看来,传递的迭代器实际上并非来自this->items,而是来自其他容器。

另一种可能性是delete *i_item导致问题,并且它只在最后一行显示为延迟反应。

编辑:另一种比你预期更频繁出现的选择是你的sort谓词不服从严格的弱序。你可以为它显示代码吗?

答案 3 :(得分:0)

经过一些测试后,我认为i_new_end调用无效.erase()

根据documentation应该仍然有效 - 只有i_new_end之后的指针才会失效。但VisualC ++显然不遵循这一点。它几乎做正确的事情,i_new_end和end()都指向内存中的相同位置,但_Myconti_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提到的记忆问题。