删除重复项

C++, Remove duplicate items

本文关键字:删除      更新时间:2023-10-16

这段代码有什么问题?我正在尝试从用户定义的容器中删除重复元素。

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]??)我不理解这种奇怪的行为……

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

当您delete所有重复的值时,您将销毁序列中保留的一些对象。

第二次排序(这是不必要的,因为unique在删除重复项时不会重新排列东西)访问每个对象,因此它立即步进那些只是delete d的对象。

一个可能的解决方案是sort两个范围内的指针由unique产生。使用set_difference( i_new_end, it_end, i_begin, i_new_end, i_sequence_inserter )来找到真正需要被释放的对象——假设它们在其他地方没有被使用。

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


编辑:

请参阅我的注释——最好的解决方案可能是完全消除指针的使用。

无论如何,这里是一个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 );
);

听起来上次调用std::sort时使用的迭代器可能无效。可能是it_begin。你可以尝试使用this->items.begin()代替吗?

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

另一种可能性是delete *i_item引起了问题,它只在最后一行显示为延迟反应。

编辑:另一个出现的方式比你期望的更频繁的选项是,你的sort谓词不遵守严格的弱排序。你能出示一下密码吗?

经过一点测试,我认为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提到的内存问题。