在std::sort中传递类方法与函数的区别

4
在一个类中,我试图通过传递同一类的方法来对向量进行排序。但在编译时它会给出错误提示。有人能告诉我问题出在哪里吗?谢谢!
它给出了以下错误: 类型为“bool (Sorter::)(D&, D&)”的参数与“bool (Sorter::*)(D&, D&)”不匹配
我还尝试过使用sortBynumber(D const& d1, D const& d2)。
#include<vector>
#include<stdio.h>
#include<iostream>
#include<algorithm>

class D {
      public:                   
             int getNumber();            
             D(int val);
             ~D(){};
      private:
              int num;
};

D::D(int val){
         num = val;
         };

int D::getNumber(){
    return num;
};


class Sorter {
      public:                   
             void doSorting();  
             bool sortByNumber(D& d1, D& d2);
             std::vector<D> vec_D;          
             Sorter();
             ~Sorter(){};
      private:
              int num;
};

Sorter::Sorter(){                 
        int i;
        for ( i = 0; i < 10; i++){
            vec_D.push_back(D(i));
           }
         };

bool Sorter::sortByNumber(D& d1, D& d2){
     return d1.getNumber() < d2.getNumber();
     };

void Sorter::doSorting(){
     std::sort(vec_D.begin(), vec_D.end(), this->sortByNumber);
     };




int main(){    
    Sorter s;
    s.doSorting();

    std::cout << "\nPress RETURN to continue...";
    std::cin.get();

    return 0;
}

1
为什么你想把 sortByNumber 定义为成员函数?它同样可以作为自由函数。 - Naveen
你不必显式地声明 D 的析构函数。编译器会免费为您提供完全相同的代码。少写代码,减少错误。 ;) 另外,我看到 Sorter::vector 是公共的。这只是一个观察;可能不是您想要的。另一件事:如果您知道向量的大小,则最好在 Sorter 的初始化列表中指定它,以避免重新分配。其他人已经谈论了const正确性的重要性,所以您应该注意这一点。最后一件事:在那个for循环中,您应该将 i 定义为循环内的局部变量。 - wilhelmtell
4个回答

6

Sorter::sortByNumber设置为静态方法。由于它没有引用任何对象成员,您不需要更改其他任何内容。

class Sorter {
public:                   
    static bool sortByNumber(const D& d1, const D& d2);
    ...
};

// Note out-of-class definition does not repeat static
bool Sorter::sortByNumber(const D& d1, const D& d2)
{
    ...
}

你应该使用常量引用,因为sortByNumber不应该修改对象。


嗯,我尝试了这个,但现在它给出以下错误:无法声明成员函数 static bool Sorter::sortByNumber(D&, D&)' 具有静态链接`。 - memC
@memC - 在定义本身之前不要使用 static。只在 class 内部的声明之前使用 static - R Samuel Klatchko
@Samuel:感谢你的更新!我之前已经将类外定义声明为静态了。所以进行了更改。但是,将D&更改为const D&会出现以下错误:const D'作为this'参数传递给int D::getNumber()'会丢弃限定符`。 - memC
@memc - 你有两个选择。你可以将 D::getNumber() 声明为 const(即 int D::getNumber() const),或者你可以让 sortByNumber 通过值传递对象(即 static bool sortByNumber(D d1, D d2))。 - R Samuel Klatchko

4

除非你有充分的理由,否则只需为你要排序的项目类型定义operator<,然后完成它:

class D { 
    int val;
public:
    D(int init) : val(init) {}
    bool operator<(D const &other) { return val < other.val; }
};

class sorter { 
    std::vector<D> vec_D;
public:
    void doSorting() { std::sort(vec_d.begin(), vec_D.end()); }
};

你所编写的 sorter 类取决于对 D 类的内部了解,以至于它们实际上就是一个类(例如,看起来两个类都无法在没有另一个类的情况下完成任何任务)。
猜测一下,你的 sorter 可能是你真实代码的一个简化版本。 SortByNumber 让人觉得原始代码可能支持多种不同类型的键,类似于:
class D { 
    std::string name;
    int height;
    int weight;
// ...
};

如果您想按名称、身高或体重对 D 对象进行排序,那么比较仍然与 D 类相关。在这种情况下,我可能会将它们放入一个公共命名空间中:

namespace D { 
class D { 
    std::string name;
    int height;
    int weight;
public:
    friend class byWeight;
    friend class byHeight;
    friend class byName;
    // ...
};

struct byWeight { 
   bool operator()(D const &a, D const &b) { 
       return a.weight < b.weight;
   }
};

struct byHeight {
    bool operator()(D const &a, D const &b) { 
        return a.height < b.height;
    }
};

struct byName { 
    bool operator()(D const &a, D const &b) { 
        return a.name < b.name;
    }
};
}

那么排序可能会像这样:
std::vector<D::D> vec_D;

// sort by height:
std::sort(vec_D.begin(), vec_D.end(), D::byHeight());

// sort by weight:
std::sort(vec_D.begin(), vec_D.end(), D::byWeight());

// sort by name:
std::sort(vec_D.begin(), vec_D.end(), D::byName());

请注意,这里不使用自由函数。对于这种情况,通常更喜欢使用函数对象。我还使用了一个命名空间来展示被排序的对象和不同排序方式之间的关联。你可以将它们变成嵌套的类,但我通常更喜欢公共命名空间(尽量保持耦合度合理)。无论如何,如果可以避免,我不会通过对象的公共接口给出原始数据的访问权限(包括只读访问权限)(在这种情况下,确实可以避免)。

2

我认为 sortByNumber() 没有必要成为一个成员函数。当它是一个成员函数时,它可以访问一些不需要的东西(因此不应该访问)。可以将该方法提取出来并重构成一个函数对象:

struct sortByNumber {
    bool operator()(const D& d1, const D& d2) const {
        return d1.getNumber() < d2.getNumber();
    }
};

或将其变为自由函数。如果可以选择,应该优先选择函数对象,因为这样编译器可以选择将代码内联。然后,您可以按以下方式排序:

std::sort(vec_D.begin(), vec_D.end(), sortByNumber());

话虽如此,您可以使用 boost::bind() 来编译原始代码:

std::sort(vec_D.begin(), vec_D.end(),
          boost::bind(&Sorter::sortByNumber, this, _1, _2));

要使其工作,您需要使用boost库,并且需要#include <boost/bind.hpp>


回复:bind1st - 你应该尝试编译这些东西。首先,没有适用于具有两个参数的方法的mem_fun。(但是您可以使用更高级的std::tr1::bind来编译这些内容。) - UncleBens
@UncleBens :哦,匆忙啊!感谢您指出这一点。是的,我应该更经常地咨询我的编译器,让我的过度自信偶尔休息一下。 - wilhelmtell

1

我认为将 sortByNumber 设为 Sorter 类的成员函数没有必要。如果你将其设为自由函数,可以更轻松地进行排序,避免所有丑陋的绑定代码。此外,在代码中应尽可能使用 const。以下是使用自由函数进行操作的示例:

首先将 int getNumber() 更改为常量函数,即 int getNumber() const;

然后再编写自由函数 sortByNumber,并通过常量引用传递参数。 bool sortByNumber(const D& d1, const D& d2);

您可以调用 sortstd::sort(vec_D.begin(), vec_D.end(), sortByNumber);


嗨,Naveen,这种排序仅在此类内完成。因此,我认为最好将其放在类内而不是作为自由函数。但似乎将其作为类函数会有相当大的开销。 - memC
1
@memC 这不是决定函数是否应该成为成员的方式。当您将函数设置为成员时,实际上提高了其权限:您使其可以访问类的所有受保护和私有成员。因此,类成员不是“与类相关的事物”,而是“需要访问类内部的事物”。如果类需要某些特殊服务,则可以创建一个静态链接的自由函数。也就是说,该函数将在类的编译单元中本地链接。通过将自由函数放置在未命名的命名空间中来实现这一点。 - wilhelmtell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接