可调用对象function类 函数指针 用于指向普通成员函数和静态成员函数
定义与使用
1 int (*funcPtr)(int , int );
使用函数指针的实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> int (*funcPtr)(int , int );int add (int a, int b) { return a + b; } int main () { funcPtr = add; funcPtr (1 , 2 ); return 0 ; }
优点与局限性 优点:
局限性:
不能捕获上下文(如lambda中的闭包)。
语法相对复杂,尤其是指针的声明和使用。
仿函数(Functors) 仿函数 ,又称为函数对象 ,是在C++中重载了operator()的==类或结构体实例==,仿函数不仅可以像普通函数一样被调用,还能携带状态。
定义和使用 代码实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> struct Adder { int to_add; Adder (int value) : to_add (value){} int operator () (int x) { return x + to_add; } }; int main () { Adder adder (5 ) ; adder (10 ); std::cout<<"5+10=" <<adder (10 )<<std::endl; return 0 ; }
特点
携带状态 :仿函数可以拥有内部状态,通过成员变量存储数据,使其在调用时具备上下文信息。
灵活性高 :可以根据需要添加更多的成员函数和变量,扩展功能。
性能优化 :编译器可以对仿函数进行优化。
仿函数应用场景 1.高级实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> struct Accumulator { int sum; Accumulator () : sum (0 ){} void operator () (int x) { return sum += x; } }; int main () { Accumulator acc; acc (10 ); acc (20 ); acc (30 ); std::cout << acc.sum << std::endl; return 0 ; }
2.使用仿函数的标准库算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> #include <vector> #include <algorithm> struct IsGreaterThan { int threshold; IsGreaterThan (int t) : threshold (t){} bool operator () (int x) const { return x > threshold; } }; int main () { std::vector<int > numbers = {1 , 5 , 10 , 15 , 20 }; IsGreaterThan greaterThan10 (10 ) ; auto it = std::find_if (numbers.begin (), numbers.end (), greaterThan10); if (it != numbers.end ()){ std::cout << "第一个大于10的数是:" << *it << std:;endl; }else { std::cout << "没有找到大于10的数" << std:;endl; } return 0 ; }
find_if()是在迭代器指定范围内查找第一个满足自定义条件的元素,前两个参数传入查找范围的迭代器,第三个参数是一元谓词 ,他是一个可调用对象(函数,Lambda,函数对象等),作用是定义查找查找条件 ,接受遍历到的元素作为参数,返回bool。
3.仿函数与模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <iostream> #include <vector> #include <algorithm> template <typename T>struct Compare { bool operator () (const T& a, const T& b) { return a < b; } }; int main () { std::vector<int > numbers = {5 , 2 , 8 , 1 , 9 }; std::sort (numbers.begin (), numbers.end (), Compare <int >()); for (auto it : numbers) { std::cout << num << " " ; } retuurn 0 ; }
std::sort主要使用快速排序
4.仿函数的优势
5.何时使用仿函数
需要携带状态时:当回调函数需要维护内部状态时,仿函数是理想选择。
复杂操作:当简单的函数指针或Lambda难以表达复杂逻辑时。
性能关键场景:由于仿函数可以被编译器优化,适用于性能敏感的代码。
Lambda表达式 Lambda表达式是C++11引入的一种轻量级函数对象,允许在代码中定义匿名函数。它们可以捕获周围的变量,具有更强的表达能力。
基本语法 1 2 3 [captures](parameters) -> return_type{ }
captures:捕获外部变量的方式,可以是值捕获、引用捕获或者混合捕获。
parameters:参数列表。
return_type:返回类型,可以省略(省略后需把->一起删掉),编译器会自动推导。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> #include <vector> #include <algorithm> int main () { int threshold = 5 ; std::vector<int > numbers = {1 ,6 ,3 ,8 ,2 ,7 }; auto new_end = std::remove_if (numbers.begin (), numbers.end (), [threshold](int n){ return x < threshold; }); numbers.erase (new_end, numbers.end ()); for (auto & n : numbers){ std::cout<< n << " " ; } std::cout<<std::endl; return 0 ; }
示例中,remove_if()是把大于等于5的数字移动到容器前,前面小于5的数字会被覆盖掉,
在C++中, remove_if 是一个标准库算法,用于移除容器中满足特定条件的元素。它不会真正从容器中删除元素,而是将不满足条件的元素移动到容器的前面,并返回一个指向新逻辑末尾的迭代器吗,可以结合 erase 方法来实现真正的删除。
捕获方式
值捕获 ([=]):捕获所有外部变量的副本,若要修改需加mutable关键字(修改不会影响原变量)。
引用捕获 ([&]):捕获所有外部变量的引用,直接操作原变量,无需mutable关键字即可修改。
混合捕获 :指定部分变量按值捕获,部分按引用捕获,如 [=, &var] 或 [&, var] 。
无捕获 ([]):不捕获任何外部变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> #include <memory> struct Adder { int to_add; Adder (int value) : to_add (value){} int operator () (int x) { return x + to_add; } void add (int x) { to_add += x; } }; int main () { auto add_ptr = std::make_shared <Adder>(10 ); auto lambda1 = [add_ptr](int x){ add_ptr->add (x); std::cout << add_ptr.use_count () << std::endl; }; lambda1 (5 ); return 0 ; }
可变Lambda 默认情况下,Lambda表达式是不可变的( const )。通过 mutable 关键字,可以允许修改捕获的变量副本。(不常用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> int main () { int count = 0 ; auto increment = [count]() mutable { count++; std::cout << "Count inside Lambda: " << count << std::endl; }; increment (); increment (); std::cout << "Count outside Lambda: " << count << std::endl; return 0 ; }
捕获成员函数和类变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> #include <vector> #include <algorithm> class Processor { public : Processor (int x) : _threshold(x) {} void process (std::vector<int >& data) { std::cout << "处理前的数据:" << std::endl; for (auto num : data) std::cout << num << " " ; std::cout<<std::endl; data.erase (std::remove_if (data.begin (), data.end (), [this ](int n){ return n < _threshold; }), data.end ()); std::cout << "处理后的数据:" << std::endl; for (auto num : data) std::cout << num << " " ; std::cout<<std::endl; } private : int _threshold; }; int main () { std::vector<int > numbers = {1 , 6 , 3 , 8 , 2 , 7 }; Processor proc (5 ) ; proc.process (numbers); return 0 ; }
Lambda与标准库算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostrea> #include <vector> #include <algorithm> int main () { std::vector<int > numbers = {4 , 2 , 5 , 1 , 3 } std::sort (numbers.begin (), numbers.end (), [](int a, int b) -> bool { return a < b; }); std::cout<< "排序后的数字:" ; for (auto num : numbers){ std::cout<< num << " " ; } std::cout << std::endl return 0 ; }
Lambda表达式的优势
std::function 对象 std::function 是C++11提供的一个通用的可调用包装器,能够封装任何可调用对象,包括普通函数、Lambda表达式、函数对象以及绑定表达式。它实现了类型擦除,使得不同类型的可调用对象可以通过统一的接口进行操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <functional> int add (int a, int b) { return a + b; } struct Multiply { int operator () (int a, int b) const { return a * b; } }; int main () { std::function<int (int , int )> func1 = add; std::cout << "Add: " << func1 (3 , 4 ) << std::endl; std::function<int (int , int )> func2 = [](int a, int b) -> int { return a - b; }; std::cout << "Subtract: " << func2 (10 , 4 ) << std::endl; Multiply multiply; std::function<int (int , int )> func3 = multiply; std::cout << "Multiply: " << func3 (3 , 4 ) << std::endl; return 0 ; }
特点
用法场景
回调函数的传递。
事件处理系统。
策略模式的实现。
示例:回调机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> #include <functional> using Callback = std::function<void (int )>;void triggerEvent (Callback cb, int value) { cb (value); } int main () { triggerEvent ([](int x) { std::cout << "事件触发,值为:" << x << std::endl; }, 42 ); struct Printer { void operator () (int x) const { std::cout << "Printer打印值:" << x << std::endl; } } printer; triggerEvent (printer, 100 ); return 0 ; }
存储和调用不同类型的可调用对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> #include <functional> #include <vector> int add (int a, int b) { return a + b; } struct Multiply { int operator () (int a, int b) const { return a * b; } }; int main () { std::vector<std::function<int (int , int )>> operations; operations.emplace_back (add); operations.emplace_back (Multiply ()); operations.emplace_back ([](int a, int b) -> int { return a - b; }); for (auto & op : operations) { std::cout << op (10 , 5 ) << " " ; } std::cout << std::endl; return 0 ; }
std::bind 操作std::bind:C++11中提供的一个函数适配器,用于绑定函数或可调用对象的部分参数,生成一个新的可调用对象。它允许提前固定某些参数,简化函数调用或适应接口需求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> #include <functional> int add (int a, int b) { return a + b; } int main () { auto add10 = std::bind (add, 10 , std::placeholders::_1); std::cout << "10 + 5 = " << add10 (5 ) << std::endl; return 0 ; }
bind头文件也在<functional>里。
占位符(std::placeholders) std::bind 使用占位符来表示未绑定的参数,这些占位符决定了在生成的新函数对象中如何传递参数。
常用占位符包括:
std::placeholder::_1
std::placeholder::_2
std::placeholder::_3
等等,最多10个。
与Lambda表达式的对比 std::bind 曾在C++11中广泛使用,但随着Lambda表达式的普及,很多情况下Lambda更为直观和高效。不过,在某些复杂的参数绑定场景下, std::bind 依然有其独特优势。
使用std::bind
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <iostream> #include <functional> int multiply (int a, int b) { return a * b; } int main () { auto multiplyBy2 = std::bind (multiply, 2 , std::placeholders::_1); std::cout << "2 * 5 = " << multiplyBy2 (5 ) << std::endl; return 0 ; }
使用Lambda表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> #include <functional> int multiply (int a, int b) { return a * b; } int main () { auto multiplyBy2 = [](int b) -> int { return multiply (2 , b); }; std::cout << "2 * 5 = " << multiplyBy2 (5 ) << std::endl; return 0 ; }
使用std::bind绑定成员函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> #include <functional> class Calculator {public : int multiply (int a, int b) const { return a * b; } }; int main () { Calculator calc; auto multiplyBy5 = std::bind (&Calculator::multiply, &calc, 5 , std::placeholders::_1); std::cout << "5 * 3 = " << multiplyBy5 (3 ) << std::endl; return 0 ; }
类的成员函数需要加取地址符(&)
使用Lambda表达式绑定成员函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> #include <functional> class Greeter {public : void greet (const std::string& name) const { std::cout << "Hello, " << name << "!" << std::endl; } }; int main () { Greeter greeter; auto greetFunc = [&greeter](const std::string& name) { greeter.greet (name); }; greetFunc ("Alice" ); return 0 ; }
如果绑定的成员函数有返回值在Lambda表达式中要return。
绑定静态成员函数 静态成员函数不依赖于类的实例,可以像普通函数一样使用 std::bind 和 std::function。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> #include <functional> class Logger {public : static void log (const std::string& message) { std::cout << "Log: " << message << std::endl; } }; int main () { auto logFunc = std::bind (&Logger::log, std::placeholders::_1); logFunc ("This is a static log message." ); return 0 ; }
绑定带有返回值的成员函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> #include <functional> class Math {public : double power (double base, double exponent) const { double result = 1.0 ; for (int i = 0 ; i < static_cast <int >(exponent); ++i) { result *= base; } return result; } }; int main () { Math mathObj; auto powerOf2 = std::bind (&Math::power, &mathObj, 2.0 , std::placeholders::_1); std::cout << "2^3 = " << powerOf2 (3 ) << std::endl; return 0 ; }
注意事项
对象生命周期 :绑定成员函数时,确保对象在可调用对象使用期间依然存在,以避免悬空指针问题。
指针与引用 :可以通过指针或引用传递对象实例给 std::bind或Lambda表达式。
捕获方式 :在使用Lambda表达式时,选择合适的捕获方式(值捕获或引用捕获)以确保对象的正确访问。