用 sort() 对成员向量进行排序的比较函数

Posted

技术标签:

【中文标题】用 sort() 对成员向量进行排序的比较函数【英文标题】:Comparison function for sorting member vector with sort() 【发布时间】:2011-04-30 09:52:12 【问题描述】:

我有这样的课:

class test
  
    vector<expr*> _exprs;  

    bool cmp(expr* e1, expr* e2);   

    ExprManager* mg;  
  

比较函数是:

bool test::cmp(expr* e1, expr* e2)  
  
    return exprHight(mg, e1) < exprHight(mg, e2);  
  

然后当我使用比较函数对_exprs(在另一个成员函数中)进行排序时

sort(_exprs->begin(), _exprs->end(), cmp);  

编译报告:

error: argument of type ‘bool (test::)(void*, void*)’ does not match ‘bool (test::*)(void*, void*)’

如何解决?感谢您的提前。

【问题讨论】:

这不是 actual 类定义,是吗?从您的 cmets 来看,我猜它有一个析构函数,但没有复制构造函数或赋值运算符。对吗? 我定义了一个用外部mg实例化的内部类,并在这个内部类中定义了operator()。现在可以使用了。 在我看来,这听起来像是一种不必要的复杂方法来解决损坏的类设计;)如果您的类在复制时泄漏资源(或双重释放它们),您将遇到类似的问题90% 的标准库。您应该从根本上解决问题。 【参考方案1】:

定义排序谓词的常规方法是使用仿函数。

让您的类定义operator() 进行比较,您只需将类的一个实例传递给std::sort

真的,你需要做的就是这样定义类:

class test
  
    vector<expr*> _exprs;  

    bool operator()(expr* e1, expr* e2);   

    ExprManager* mg;  
  

然后你可以像这样调用sort

sort(_exprs->begin(), _exprs->end(), test());  

或者,当然,使用test 类的现有实例,而不是构造一个新实例。但是你只是传入一个类实例,根本不需要提及成员函数。

如果排序发生在另一个成员函数中(从您对 _exprs 的引用看起来是这样),请写

sort(_exprs->begin(), _exprs->end(), *this);  

需要注意的一点是,std::sort 与大多数其他标准库算法一样,会复制谓词对象,因此您的谓词类必须能够安全地处理复制(您的类应该始终这样做无论如何 em>)

简而言之,实现这一点的方法就是遵循“三法则”。

如果您的类定义了析构函数、复制构造函数赋值运算符,那么它几乎肯定应该定义所有三个。

如果使用编译器生成的复制构造函数,它只会复制你的类的指针成员,所以你会有两个对象包含指向同一个对象的指针。

如果类有一个析构函数在该指针上调用delete,那么最终会执行两次。这是一个错误。

如果您的类旨在可复制(并且大多数标准库都要求这样做),那么您必须定义适当的复制构造函数和赋值运算符以安全地实现它(例如,复制指针指向的资源,或者请改用智能指针)。

如果你的类不是是可复制的,那么你应该将复制构造函数和赋值运算符定义为private,这样复制类的尝试将导致编译时错误,而不是运行时崩溃。

你应该永远定义一个可以被复制的类,但这样做不正确。要么定义必要的复制构造函数/赋值操作符/析构函数来处理复制,要么通过将复制操作符/赋值操作符设为私有来使复制变得不可能。

将指向类所拥有的动态分配内存的指针包装在智能指针中是一种免费获得可复制性的简单方法。

如果类只包含 RAII 对象,那么你根本不需要定义析构函数,所以三规则也不需要你定义复制构造函数和赋值运算符。

【讨论】:

@hailin:我编辑了我的答案,以更好地描述您在 cmets 中提到的双重删除问题的正确解决方案。 当黄金法则是谓词应该非常复制便宜时,您会花费大量时间谈论类的复制 :) 伙计, 谓词 is 昂贵 :) 请记住,标准要求 std::vector 的复制构造函数进行 deep 复制!... @mmutz: 是的,谓词复制起来应该很便宜,但是一个重要的规则是它们应该是安全复制的。鉴于 OP 的问题,他似乎不知道如何实现这一目标。一旦他了解了如何使对象安全地可复制,他显然也应该制作一个更便宜的谓词函子。 :) 首先让它工作,然后让它快速。 :)【参考方案2】:

你需要使test::cmp()静态

class test 
    // ...
    static bool cmp( expr * lhs, expr * rhs );
;

或者,如果test::cmp() 不能由于某种原因是静态的,则需要使用(boost::std::tr1::bind() 来绑定(隐式)this 参数cmp():

test * someInstance = this // other // something;
sort( _exprs->begin(), _exprs->end(),
      bind( &test::cmp,  someInstance, _1, _2 ) );

【讨论】:

我试过这个,但是发生了另一个编译器错误-错误:在静态成员函数中无效使用成员'test::vc_'【参考方案3】:

使用 operator() 然后将 '*this' 作为谓词传递给排序算法。

class test
  
    vector<expr*> _exprs;  

    bool operator()(expr* e1, expr* e2);   

    ExprManager* mg;  
  

sort(_exprs->begin(), _exprs->end(), *this);  

无论您作为谓词传递什么,都必须具有公共可访问性,

【讨论】:

运算符的定义和你之前的test::cmp一样 编译正常,但出现段错误(双释放错误)。我用 valgrind 追踪。 valgrind 表示在 stl_algo.h 中的某处调用了破坏。 检查cmp / operator()函数中使用的函数调用。他们可能正在释放一些东西 @hailin:那么您的代码还有另一个错误。这是正确的做法。 sort 本身不会调用 newdelete,因此任何被删除的事情都是由于对象超出范围而发生的。很可能,您没有遵循三个规则。您的类可能有一个析构函数,但没有复制构造函数。 (谓词对象被sort 复制,因此它必须是可正确复制的,不会泄漏资源或使其自身处于不一致状态。 该类有析构函数但没有复制构造函数。我认为创建了时间对象,并且在 sort() 函数退出时删除了其他成员(指针类型)。

以上是关于用 sort() 对成员向量进行排序的比较函数的主要内容,如果未能解决你的问题,请参考以下文章

用于对包含指向自定义类对象的指针的向量进行排序的比较器

编写一个sort函数,它用于对任何类型的数组进行排序

MATLAB学习七:数组比较sort

如何用sort对结构体进行排序

C++ 使用类对列上的 2d 向量进行排序失败(带比较功能的问题)

C++ STL中SORT的自定义比较函数?