我正在尝试使用谓词函数对对象矢量进行排序,并且我得到了一些段错误...
我有一个班级Item
和一个vector< Item > _items
项目列表。我需要根据显示顺序(类的数字成员)对它进行排序,我只是使用谓词函数调用一个简单的排序。
sort(_items.begin(), _items.end(), sort_item_by_display_order);
其中谓词函数是
bool sort_item_by_display_order (Item i, Item j)
{
return i.GetDisplayOrder()>j.GetDisplayOrder();
}
和GetDisplayOrder是
int Item::GetDisplayOrder()
{
return display_order;
}
但是......我这样做时遇到了一些段错误。然后我在谓词函数中添加了一个计数器来检查它被调用了多少次,我发现当它崩溃时,计数器大于向量的大小。
经过一些阅读后,我更改了代码以使用迭代器而不是使用.begin()和.end()(这不应该是相同的吗?!)
所以我现在拥有的是
vector<Item>::iterator it_start, it_end;
it_start = _items.begin();
it_end = _items.end();
sort(it_start, it_end, sort_item_by_display_order);
具有相同的谓词函数。
现在它没有崩溃,但是......对于大多数排序我做了更多的迭代然后我正在排序的矢量大小(这可能是正常的)
所以...使用_items.begin()
或_it_start
调用排序有什么区别。据我所知,他们是一样的吗?!
还有一点需要注意。 Item
是一个简单的基类,声明为
class Item
{
private:
(...)
public:
(...)
}
作为参考,我使用了http://www.cplusplus.com/reference/algorithm/sort/和http://www.codeguru.com/forum/showthread.php?t=366064。
在第二个链接中,他们添加了一个const和&amp;到谓词函数参数,这将使我的函数像这样
bool sort_item_by_display_order (const Item& i, const Item& j)
{
return i.GetDisplayOrder()>j.GetDisplayOrder();
}
但是我收到编译错误:
Item.cpp|1485|error: passing `const Item' as `this' argument of `int Item::GetDisplayOrder()' discards qualifiers|
arghhh ......问题是......我做错了什么?
答案 0 :(得分:4)
首先,调用比较函数的次数与集合中的元素相比更为正常。例如,当我们说排序算法的复杂度是O( n log n )时,这意味着什么。对大小 n 的集合执行的比较次数约为 n ×log( n )。 (事实上, n 几乎是最小次调用它;否则,我们甚至无法判断该集合是否已经在第一名。)
其次,当您将参数设置为const引用时会出现错误,因为您已将GetDisplayOrder
声明为非const方法。您不允许在const对象上调用非const成员函数,因为编译器假定该方法将尝试修改该对象,即使在这种情况下它不会修改任何内容。将const
添加到声明和定义的末尾:
int GetDisplayOrder() const;
int Item::GetDisplayOrder() const {
return display_order;
}
最后,还有分段错误的问题。您在此处显示的代码不足以查明原因。你改变传递迭代器到sort
的方式是正确的,这不应该有任何影响。我怀疑你的Item
类需要一个复制构造函数和赋值运算符,但它们要么没有实现,要么它们没有正确实现。对矢量进行排序显然涉及在集合中移动项目,这需要一个工作分配操作符。将这些项传递给原始的比较函数,它通过值而不是const引用接受参数,需要一个工作的复制构造函数。如果您正在进行任何动态内存分配(例如使用new
或malloc
),则需要确保在分配或复制对象时对内存进行“深层复制”,或者你想办法让多个对象共享相同的分配。如果多个对象认为它们都拥有相同的内存块,则其中一个可能会在其他内存完成之前释放该内存,这肯定会导致分段错误(当您访问释放的内存时)。
答案 1 :(得分:0)
除了Rob Kennedy所说的,尝试在std::swap
中设置断点(std::sort
实际交换元素以移动它们)。如果由于某种原因无法使用调试器,您甚至可以实现自己的swap
并进行一些“printf调试”。
当您点击断点时,请仔细观察是否正确复制了对象并确保没有任何损坏。这个想法是试图找到腐败的确切位置,这可能不一定会立即导致程序崩溃,但后来会导致不良影响。