尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

24. 函数符

24. 函数符 函数对象函数对象函数符 functor函数对象也叫函数符functor是可以以函数方式与 () 结合使用的任意对象。这包括函数名、指向函数的指针以及重载了 () 运算符的类对象即定义了 operator()() 的类。例如定义一个 Linear 类重载的 () 运算符使得能够像函数那样使用 Linear 对象f1(12.5) 的右侧就是 f1.operator()(12.5)。class Linear { // 函数符类重载 () 运算符 private: double slope; // 斜率由构造函数设置 double y0; // 截距由构造函数设置 public: Linear(double sl, double y) // 构造函数绑定状态 : slope(sl), y0(y) {} double operator()(double x) { // 重载 ()使对象可像函数调用 return y0 slope * x; // x 来自调用参数 } }; // Linear f1(1.0, 0.0); double y1 f1(12.5); // 计算 0 1*12.5for_each 的第三个参数与模板机制for_each 本质上就是一个“包装好的 for 循环”它负责从头到尾遍历容器并把每个元素依次传递给你提供的函数或函数对象去执行。for_each() 的第三个参数可以是常规函数也可以是函数符。for_each( 起始位置, 结束位置, 函数/对象 );不能把它声明为函数指针因为函数指针指定了参数类型由于容器可以包含任意类型预先无法知道应使用哪种参数类型。STL 通过使用模板解决这个问题其原型为template class InputIterator, class Function Function for_each(InputIterator first, InputIterator last, Function f);// for_each 的原型Function 可为函数指针或函数符类 template class InputIterator, class Function Function for_each(InputIterator first, InputIterator last, Function f) { while (first ! last) { // 遍历整个区间 f(*first); // 对每个元素调用 f first; } return f; // 返回函数对象 }函数符概念generator / unary / binary / predicate函数符概念Generator / Unary / Predicate是 STL 算法对“传入参数个数”和“返回值类型”的一种“硬性接口约定”。生成器generator是不用参数就可以调用的函数符一元函数unary function是用一个参数可以调用的函数符二元函数binary function是用两个参数可以调用的函数符谓词predicate是返回 bool 值的一元函数二元谓词binary predicate是返回 bool 值的二元函数。如 for_each() 的函数符应当是一元函数因为它每次用于一个容器元素一些 STL 函数需要谓词参数或二元谓词参数。remove_if 与类函数符传递额外信息形态用法是否真的释放内存改变容器大小后续必须做的事通用算法头文件algorithmstd::remove_if(v.begin(), v.end(), pred)❌否。它只是把“被删的元素”挪到末尾把“保留的元素”挪到前面容器大小不变。必须配合erase()才能真正删除即Erase-Remove 惯用法。list成员函数头文件listmy_list.remove_if(pred)✅是。链表节点被直接销毁list的大小自动变小。不需要任何后续操作。算法限制remove_if 只能接受一元谓词一个参数用于接收当前遍历到的元素。信息瓶颈普通函数只能写死阈值无法动态传入第二个参数。解决方法函数符利用类成员变量如 cutoff携带“额外信息”。构造函数负责设置规则operator() 负责结合元素和规则做出判断。template class T class TooBig { // 类函数符用成员携带额外信息 private: T cutoff; // 取舍值由构造函数设置 public: TooBig(const T t) : cutoff(t) {} // 构造函数绑定阈值 bool operator()(const T v) { // 谓词单个参数 return v cutoff; // 判断是否大于阈值 } }; // scores.remove_if(TooBigint(200)); // 删除所有大于 200 的元素类函数符作为函数适配器TooBig2如果已经有一个接受两个参数的模板函数 tooBig(val, lim)可以用类将它转换为单参数的函数对象——TooBig2 类把第二个参数用于构建函数对象operator() 内部调用二元模板函数并传入已绑定的 cutoff。template class T bool tooBig(const T val, const T lim) { // 已有的二元模板函数 return val lim; } ​ template class T class TooBig2 { // 函数适配器二元→一元 private: T cutoff; // 绑定的第二个参数 public: TooBig2(const T t) : cutoff(t) {} bool operator()(const T v) { // 单参数接口 return tooBigT(v, cutoff); // 内部调用二元函数 } }; // TooBig2int tB100(100); if (tB100(x)) // 等价于 if (tooBig(x, 100))transform() 算法transform() 算法用于对一个或两个输入区间中的每个元素执行指定操作并将计算结果写入目标区间实现“批量转换”。一元将一个区间中的每个元素通过一元函数op转换输出到另一个区间。template class InputIt, class OutputIt, class UnaryOp OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first, UnaryOp op);参数含义first1, last1输入区间的起点和终点d_first目标区间的起点输出结果op一元函数符接收一个元素返回转换后的值#include algorithm #include vector ​ std::vectorint src {1, 2, 3, 4}; std::vectorint dst(4); // 预留空间 ​ // 把 src 中的每个元素平方存入 dst std::transform(src.begin(), src.end(), dst.begin(), [](int x) { return x * x; }); // dst 变为 {1, 4, 9, 16}二元版将两个区间中对应位置的元素通过二元函数 binary_op 结合输出到目标区间。template class InputIt1, class InputIt2, class OutputIt, class BinaryOp OutputIt transform(InputIt1 first1, InputIt1 last1, InputIt2 first2, OutputIt d_first, BinaryOp binary_op);参数含义first1, last1第一个输入区间的起点和终点first2第二个输入区间的起点长度由第一个区间决定d_first目标区间的起点binary_op二元函数符接收两个元素返回结合后的值std::vectorint a {1, 2, 3}; std::vectorint b {10, 20, 30}; std::vectorint result(3); ​ // 把 a 和 b 的对应元素相加存入 result std::transform(a.begin(), a.end(), b.begin(), result.begin(), [](int x, int y) { return x y; }); // result 变为 {11, 22, 33}注意第二个输入区间必须至少有 last1 - first1 个元素否则行为未定义。目标迭代器可以等于输入迭代器实现“原地修改”。预定义的函数符头文件 functional预定义函数符是“运算符的‘函数对象’包装器”。因为STL 算法的参数是“对象”而不是“符号”。分类函数符名称对应的运算符中文含义算术plusT加法minusT-减法multipliesT*乘法dividesT/除法modulusT%取模余数negateT-一元取负如-5比较关系equal_toT等于not_equal_toT!不等于greaterT大于lessT小于greater_equalT大于等于less_equalT小于等于逻辑logical_andT逻辑与logical_orT||逻辑或logical_notT!逻辑非自适应函数符adaptable functor与函数适配器STL 有 5 个相关概念自适应生成器adaptable generator、自适应一元函数adaptable unary function、自适应二元函数adaptable binary function、自适应谓词adaptable predicate和自适应二元谓词adaptable binary predicate。使函数符成为自适应的原因是它携带了标识参数类型和返回类型的 typedef 成员result_type、first_argument_type和second_argument_type如plusint对象的返回类型被标识为plusint::result_type。binder1st它的数学本质是“固定二元函数的一个参数把它变成一元函数。”它把2.5“绑定”到multiplies的第一个参数上生成一个新的临时对象。这个新对象只需要接收一个参数x内部执行2.5 * x。现代 CC11 起中它已经被 Lambda 表达式和 std::bind 彻底取代了。知识点描述函数对象函数符可以函数方式与()结合使用的对象函数名、函数指针、重载()的类对象类函数符用构造函数保存状态x来自operator()参数。for_each 第三参数与模板第三参数不能声明为函数指针参数类型不确定用模板参数Function同时接受函数指针与函数符类for_each内部用f()统一调用。函数符概念生成器无参数、一元函数一个参数、二元函数两个参数、谓词返回 bool 的一元函数、二元谓词返回 bool 的二元函数for_each需要一元函数。remove_if 与类函数符list::remove_if(predicate)对每个元素应用谓词返回 true 则删除谓词只能一个参数用类成员构造函数设置 cutoff传递额外信息。类函数符作为函数适配器用类把二元模板函数转换为单参数函数对象第二参数由构造函数绑定、operator()内部调用二元函数TooBig2是函数适配器使函数满足不同接口。transform() 算法一元版对区间每元素施加函数符生成新序列二元版对两个区间对应元素施加二元函数目标迭代器可在原区间内覆盖旧值。预定义的函数符functional提供与内置算术/关系/逻辑运算符等价的函数符plus、multiplies、greater 等因运算符不是函数用plusdouble()等作为算法参数老式实现用times代替multiplies。自适应函数符与函数适配器自适应函数符携带result_type、first_argument_type、second_argument_typetypedefbinder1st/binder2nd将自适应二元函数适配为一元绑定第一/第二参数助手函数bind1st()/bind2nd()简化创建。
返回列表