在C++中对对象向量进行排序

8

假设我有一个名为"Information"的类,它在一个向量中存储了人们的姓名和年龄。

因此...

class Information {

private:
int age;
string name;

//etc, etc...
};

我应该如何根据年龄升序/降序对向量进行排序?
我认为你可以使用类似下面的代码:
sort(listOfPeople.begin(), listOfPeople.end(), greater<Information>());

listOfPeople将是一个向量。

非常感谢您的帮助。

6个回答

6
如果您想按年龄非降序排序,一种方法是定义一个比较器函数:
class CompareInformations {
    public:
    // after making CompareInformations a friend class to Information...
    operator(const Information& rhs, const Information& lhs) {
        return rhs.age < lhs.age;
    }
};

然后进行排序:

sort(listOfPeople.begin(), listOfPeople.end(), CompareInformations());

你可以为你的类重载operator<,而不需要比较对象:
// inside your class
bool operator <(const Information& rhs) {
    return age < rhs.age;
}

然后进行排序:
sort(listOfPeople.begin(), listOfPeople.end());

上面的示例假设您想按非降序(几乎是升序,但不完全是)对其进行排序。要按非升序排列,只需将所有出现的 "<" 改为 ">" 即可。

将CompareInformations设置为Information的友元类,这句话是什么意思? - user432584920684
是的,我使用了 rhs.getAge()。我尝试编译,但是出现了以下错误信息: error: passing 'const Information' as 'this' argument of 'int Information::getAge()' discards qualifiers testone.cpp - user432584920684
@Vincent,你试过将 getAge 方法标记为 const 吗?只有 const 函数才能在 const 对象上调用。或者从比较函数的参数中删除 const - Seth Carnegie
@Vincent,你试图将Ransom的答案和我的答案混合在一起。如果你使用像Ransom的答案那样的普通函数版本(即没有类或运算符重载),那么请将sort(listOfPeople.begin(), listOfPeople.end(), CompareAges());更改为sort(listOfPeople.begin(), listOfPeople.end(), CompareAges);。注意我删除了CompareAges后面的括号。 - Seth Carnegie
1
@Vincent 你应该看一下C++ FAQ: Const correctness,特别是什么是“const成员函数”?。实际上,当你有时间的时候,可以阅读整个FAQ,它是一个非常好的资源。 - user887210
显示剩余4条评论

4
您需要创建一个比较函数或者函数对象类,该函数接收两个 Information 引用并且在第一个引用应该排在第二个引用之前时返回 true
下面的代码将按照从年长到年轻的顺序排序:
bool CompareAges(const Information & left, const Information & right)
{
    return left.age > right.age;
}

std::sort(listOfPeople.begin(), listOfPeople.end(), CompareAges);

为了选择是按升序还是降序排序,您可以用不同的比较函数调用两个不同的 sort,或者创建一个有标志来决定如何排序项目的函数对象类。
struct CompareAgesUpOrDown
{
    CompareAgesUpOrDown(bool bDown) : m_bDown(bDown) {}
    bool operator() (const Information & left, const Information & right)
    {
        if (m_bDown)
            return left.age < right.age;
        else
            return left.age > right.age;
    }
    bool m_bDown;
};

bool bDown = ...;
std::sort(std::sort(listOfPeople.begin(), listOfPeople.end(), CompareAgesUpOrDown(bDown));

我不确定我是否做得正确,但尝试实现第一个代码块时,在尝试编译testone.cpp时会给我以下输出:testone.cpp:27: error: passing 'const Information' as 'this' argument of 'int Information::getAge()' discards qualifiers testone.cpp:27: error: passing 'const Information' as 'this' argument of 'int Information::getAge()' discards qualifiers。 - user432584920684
抱歉,我在这方面还比较新。我应该如何将 getAge 标记为 const? - user432584920684
1
你可以在函数原型的末尾加上 const 来将一个函数标记为常量函数:int getAge() const。这样做就是在“承诺”该函数不会修改其所属类的任何成员。 - user887210
我明白了。非常感谢你的帮助。 我想这也适用于比较time_t而不是int吧? 再次感谢! - user432584920684
@Vincent 是的,你说得对,因为 time_t 是一种整数类型(它是某种数字)。 - Seth Carnegie
@Vincent,是的,time_t 应该和 int 一样好用。只要它有一个有效的 < 运算符就可以。 - Mark Ransom

4

其他人已经展示了C++98/03的解决方案。在C++11中,您可能希望使用一个lambda表达式来进行比较:

// ascending age:
std::sort(people.begin(), people.end(), 
          [](person const &a, person const &b) { return a.age < b.age; });

// descending age:
std::sort(people.begin(), people.end(), 
          [](person const &a, person const &b) { return b.age < a.age; });

如果有这样的情况发生:

在IT技术方面,如果出现问题:

// ascending name:
std::sort(people.begin(), people.end(), 
          [](person const &a, person const &b) { return a.name < b.name; });

// descending name:
std::sort(people.begin(), people.end(), 
          [](person const &a, person const &b) { return b.name < a.name; });

在我看来,信息这个名称太过于通用了,因此我将其更改为人员。相反的,人员列表过于强调形式而非内容(更糟糕的是,它实际上是错误的,因为你真正拥有的是一个人员向量,根本不是一个列表)。在编程中,我认为通常最好只使用列表指代链表,而不是一般的线性数据结构。


谢谢,我会记住你的建议。 只是一个问题,如果您以这种方式实现它,我假设您不需要bool比较函数?当我回家后,我会尝试这段代码。 - user432584920684
@Vincent: 是的。Lambda可让您内联指定比较代码,而无需分别进行操作。顺便说一下,如果您通常使用一种排序方式,则可能希望将其放入类的 operator< 中,并仅为其他情况明确指定顺序。 - Jerry Coffin

1

你需要一个比较函数或对象才能使用该排序。请查看 cplusplus.com的排序页面以获取示例和信息。

这是一个使用比较函数的完整示例:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

class Information {
public:
  Information(int age, std::string name) : m_age(age), m_name(name) {}
  int age() const { return m_age; }
  std::string name() const { return m_name; }
private:
  int m_age;
  std::string m_name;

  friend bool sortInformationByAgeAscending(const Information& lhs, 
                                            const Information& rhs);      
  friend bool sortInformationByAgeDescending(const Information& lhs, 
                                             const Information& rhs);
};

bool sortInformationByAgeAscending(const Information& lhs, 
                                   const Information& rhs) {
  return lhs.m_age < rhs.m_age;
}

bool sortInformationByAgeDescending(const Information& lhs, 
                                    const Information& rhs) {
  return lhs.m_age > rhs.m_age;
}
int main (int argc, const char * argv[])
{
  std::vector<Information> info;
  info.push_back(Information(1, "Bill"));
  info.push_back(Information(5, "Ann"));
  info.push_back(Information(2, "Sue"));

  std::sort(info.begin(), info.end(), sortInformationByAgeAscending);

  std::cout << info.at(0).age() << ": " << info.at(0).name() << std::endl;
  std::cout << info.at(1).age() << ": " << info.at(1).name() << std::endl;
  std::cout << info.at(2).age() << ": " << info.at(2).name() << std::endl;

  return 0;
}

0

我会重载operator <,然后使用greater进行排序。Greater的本质意味着rhs < lhs。然后你可以使用sort(listOfPeople.begin(), listOfPeople.end(), greater<Information>());进行排序。

如果你决定添加operator<,你的类将能够与std::set一起使用,并作为std::map中的键,除了允许排序。

class Information {

private:
int age;
string name;

friend bool operator< (Information const& lhs, Information const& rhs){
    return lhs.age < rhs.age;
}

//etc, etc...
};

这是一个友元函数而不是成员函数的原因是什么? - Seth Carnegie
习惯性的行为。我通常处理算术类型,并希望自由函数从转换中受益。 - Flame

0

如果要进行降序排序,您必须重载 > 运算符而不是 <,然后使用 greater 选项调用 sort 函数,例如: sort(listOfPeople.begin(), listOfPeople.end(), greater<Information>());


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