--- title: "07-STL自定义排序类型二三事" created: 2025-11-25 tags: - 项目筑基 --- # STL自定义排序类型二三事 截至目前 已经接触到了很多次关于sort、set 排序方式改变的例子 也接触到了三种不同的方法 比如在哈夫曼树中采用lambda表达式(匿名函数) 去对已知无双亲结点排序寻找权最小的俩结点 ![[stlsort-image1.png]] 在list容器中 对自定义数据类型进行排序 使用函数回调的方式指定排序方式 ![[stlsort-image2.png]] ![[stlsort-image3.png]] 以及在set容器中 使用仿函数的方式指定排序方式 ![[stlsort-image4.png]] ![[stlsort-image5.png]] 在研究对于set的指定排序方式时 产生了很多疑惑 为什么它好像只有用仿函数才能更方便地实现指定排序方式 而对于sort而言却哪种方式都差不多 不由得考虑起来 这三种方式 有什么特点 适用范围如何? 先说结论 使用何种方式去指定排序方式得看时机 对于set而言 insert时就已经排好序了 所以不能够在插入时再进行排序 得在插入之前就定好排序规则 并且set可接受的参数为数据类型 这对于仿函数来说非常合适 而对于sort而言 则无需担心插入前插入后的问题 对于已经固定的数据 使用函数回调或者匿名函数都是适用的 对于 `std::set`,通常在创建时指定排序规则;对于 `std::sort`,可以在排序时指定规则,这为更灵活的排序提供了便利。 ### 1. 使用仿函数类: 创建一个自定义的仿函数类,该类必须包含一个 `operator()` 函数,以定义元素的比较规则。然后,将该仿函数类的对象传递给需要自定义排序规则的函数或容器的构造函数。 示例: ```cpp #include // 自定义仿函数类 struct MyComparator { bool operator()(int a, int b) const { return a < b; } }; int main() { // 使用仿函数类来自定义排序规则 std::set mySet; mySet.insert(5); mySet.insert(2); mySet.insert(8); return 0; } ``` ### 2. 使用 Lambda 表达式: 创建一个 Lambda 表达式,该表达式定义元素的比较规则,并将它传递给支持自定义排序规则的函数,例如 `std::sort` ```cpp #include #include int main() { std::vector vec = {5, 2, 8}; // 使用 Lambda 表达式来自定义排序规则 std::sort(vec.begin(), vec.end(), [](int a, int b) { return a < b; }); return 0; } ``` ### 3. 使用函数回调(函数指针): 创建一个可调用的函数,然后将该函数的指针传递给支持自定义排序规则的函数,例如 `std::sort`。 ```cpp #include #include // 自定义比较函数 bool customCompare(int a, int b) { return a < b; } int main() { std::vector vec = {5, 2, 8}; // 使用函数回调来自定义排序规则 std::sort(vec.begin(), vec.end(), customCompare); return 0; } ``` 这三种方法都可以用于自定义排序规则,具体选择哪种方法取决于你的需求和代码的情况。仿函数类通常用于容器(如 `std::set`)的构造时指定排序规则,而 Lambda 表达式和函数回调适用于需要在排序时指定规则的情况,例如 `std::sort` 函数。根据代码的灵活性、可读性和性能需求,选择最合适的方法。 ### 为什么set用仿函数合适 且set可接受的参数为数据类型 这对于仿函数来说非常合适 这里需要再提一嘴 卡就卡在这个地方 首先这个先指定规则不用再解释了 ![[stlsort-image6.png]] 这是仿函数较合适的原因之一 但最主要的还是因为 模板类 `std::set` 需指定一个数据类型作为其模板参数 而不是函数名 (模板参数必须是类型) 这个地方要传入类型而函数本身并不是类型,所以回调或者匿名函数不合适 ![[stlsort-image7.png]] 仿函数就是类型了? 对 C++中引入了一种称为函数对象(function object)或仿函数(functor)的概念,它是一种行为类似于函数的对象,可以像函数一样调用。函数对象是通过定义一个类并在该类中重载 operator() 来实现的。这个重载的 operator() 函数使得对象可以像函数一样被调用。 要理解仿函数是类型 先想想什么是类型 类型(type) 是一个用于表示数据的分类或种类的概念。类型定义了数据的属性、存储方式和允许进行的操作。每个变量、表达式和对象都有一个类型,它决定了它们可以存储什么样的数据以及可以对其执行什么样的操作。那么仿函数为什么是类型?仿函数本身是一个类(class)的实例,而类是一种用户自定义的数据类型。仿函数类中包含了一个或多个函数调用运算符(operator())的重载,使得这个类的对象可以像函数一样被调用。因此,仿函数类实际上是一个用户自定义的数据类型,它具有函数的行为。可以理解成:仿函数是一种用户自定义的数据类型,它允许我们将函数的行为封装到对象中,并通过对象的方式来调用这些行为。 以及在该仿函数实现中 还遇到了一个问题 ![[stlsort-image8.png]] 需要加const 为什么? 在 C++ 中,如果希望一个成员函数能够被 const 对象调用,这个成员函数本身必须被声明为 const 成员函数。如果不加 const,则这个成员函数只能被非 const 对象调用。这是因为在 C++ 中,const 对象不允许修改对象的状态,所以只能调用 const 成员函数。在这里,operator() 是一个成员函数,它用于比较两个元素的大小,并不会修改 MyCompare 对象的状态。因此,将 operator() 声明为 const 成员函数使得它可以被 const 和非 const 对象调用,这样可以更灵活地使用这个仿函数。总之,将 operator() 声明为 const 成员函数是一种良好的编程实践,可以提高代码的可维护性和灵活性。这允许你在不修改对象状态的情况下使用仿函数,无论是在 const 还是非 const 上下文中。 ## 附件 > 📄 STL自定义排序类型二三事.docx:[[STL自定义排序类型二三事.docx|STL自定义排序类型二三事.docx]] --- ⬅️ [[06-深入stl|深入stl]] 🏠 [[00-编程语言|00-编程语言]] ➡️ [[08-CSharp|CSharp]]