大纲
C++ 11 的 function 类模板
概念介绍
function
是 C++ 11 引入的一个类模板,用于存储任何可以调用的目标(如普通函数、函数指针、函数对象、Lambda 表达式等),并通过统一的接口进行调用。它能够封装和管理函数,允许将函数作为对象传递和存储,位于 <functional>
头文件中,常用于回调和高阶函数。
使用说明
- 用函数类型实例化
function
类模板 - 通过
function
类模板调用 operator()
函数的时候,需要根据函数类型传入相应的参数
使用特点
- 通过类型擦除技术存储任意可调用对象(普通函数、Lambda 表达式、仿函数、成员函数指针等),无需关心具体类型,仅需关注调用签名
- 编译时会检查参数和返回类型是否匹配,避免了传统函数指针的类型不安全问题
使用场景
- 事件回调
- 作为函数参数传递可调用对象
- 存储 Lambda 表达式
对比说明
C 语言中的函数指针与 C++ 的 function
类模板的对比如下:
特性 | 函数指针 (C 语言) | function 类模板 (C++) |
---|
灵活性 | 只能指向具有匹配签名的函数 | 可以封装多种类型的可调用对象 (函数、Lambda、函数对象) |
状态管理 | 不支持状态封装 | 支持状态封装(如 Lambda 表达式和函数对象) |
类型安全 | 不提供额外的类型安全 | 提供类型安全检查 |
性能开销 | 较低 | 可能有较高的内存和性能开销 |
多态性 | 不支持多态 | 支持多态 |
C 语言中的函数指针与 C++ 的 function
类模板的区别如下:
基本概念
- 函数指针(C 语言)
- 函数指针是一个变量,用于存储函数的地址。通过该指针,可以间接调用对应的函数。
- C 语言中的函数指针需要显式指定函数签名(返回值类型和参数类型)。
function
类模板(C++):function
是 C++ 11 引入的类模板,提供了一种通用的、类型安全的方式来存储、传递和调用可调用对象(如普通函数、函数指针、Lambda 表达式、函数对象等)。- 它可以封装多种不同类型的可调用对象,并且允许它们通过统一的接口被调用。
灵活性和类型支持
- 函数指针(C 语言):
- 函数指针只能指向具有相同函数签名(参数类型和返回类型)的函数。
- 不支持封装函数对象、Lambda 表达式等其他类型的可调用对象。
function
类模板(C++):function
支持多种类型的可调用对象,包括普通函数、函数指针、Lambda 表达式、函数对象等。- 它的模板参数可以适配任意函数签名,支持更加灵活的调用。
- 还支持捕获外部状态的 Lambda 表达式,或者包含状态的函数对象。
状态管理
- 函数指针(C 语言):
- 函数指针无法封装额外的状态信息,指向的仅仅是一个函数。
- 如果需要管理状态(如在函数调用前后执行某些操作),必须依赖外部的代码来实现。
function
类模板(C++):function
可以封装状态信息。例如,Lambda 表达式和函数对象可以拥有成员变量和成员函数,允许在调用时使用封装的状态。- 这种能力使得
function
在处理回调和事件处理时更具优势。
类型安全
- 函数指针(C 语言):
- 函数指针本身类型是静态的,编译时要求函数签名必须匹配,但它不提供额外的类型安全检查,错误的使用可能导致未定义行为。
function
类模板(C++):function
是类型安全的,编译时会检查存储的可调用对象是否与声明的函数签名一致。- C++ 编译器提供了类型安全的保证,避免了函数签名不匹配带来的错误。
内存管理和性能
- 函数指针(C 语言):
- 函数指针直接存储函数的地址,不涉及额外的内存分配,因此它的性能开销较小。
- 由于没有额外的封装,也没有多态或状态的管理,性能上较为高效。
function
类模板(C++):function
需要进行额外的内存分配和类型擦除(Type Erasure)。对于 Lambda 表达式和函数对象,它会封装一个通用的接口,这可能带来额外的性能开销。- 但是,它的灵活性和类型安全是
function
的优势。
多态和扩展性
- 函数指针(C 语言):
- 函数指针没有多态性。它们只是简单地指向某个函数,无法支持运行时多态。
function
类模板(C++):function
支持通过函数对象、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 34 35 36 37
| #include <iostream> #include <functional> #include <string>
using namespace std;
void hello1() { cout << "hello world" << endl; }
void hello2(string str) { cout << "hello " << str << endl; }
int sum(int a, int b) { return a + b; }
int main() { function<void()> function1 = hello1; function1();
function<void(string)> function2 = hello2; function2("peter");
function<int(int, int)> function3 = sum; int total = function3(1, 2); cout << "total = " << total << endl;
function<int(int, int)> function4 = [](int a, int b) {return a + b; }; int total2 = function4(3, 5); cout << "total2 = " << total2 << endl;
return 0; }
|
程序运行输出的结果如下:
1 2 3 4
| hello world hello peter total = 3 total2 = 8
|
案例代码二
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> #include <functional> #include <string>
using namespace std;
class Test {
public:
void hello(string str) { cout << "hello " << str << endl; }
};
int main() { Test t; function<void(Test*, string)> function1 = &Test::hello; function1(&t, "peter"); function1(&Test(), "peter");
return 0; }
|
程序运行输出的结果如下:
案例代码三
使用 function
函数对象类型来实现图书管理系统的菜单列表选择功能。
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| #include <iostream> #include <functional> #include <string> #include <map>
using namespace std;
void doShowAllBooks() { cout << "查看所有书籍" << endl; };
void doBorrowBook() { cout << "借书" << endl; };
void doBackBook() { cout << "还书" << endl; }
void doQueryBook() { cout << "查询书籍" << endl; }
void doLoginOut() { cout << "注销" << endl; }
int main() { int choice = 0;
map<int, function<void()>> actionMap; actionMap.insert({ 1, doShowAllBooks }); actionMap.insert({ 2, doBorrowBook }); actionMap.insert({ 3, doBackBook }); actionMap.insert({ 4, doQueryBook }); actionMap.insert({ 5, doLoginOut });
for (;;) { cout << "\n-------------------" << endl; cout << "1. 查看所有书籍" << endl; cout << "2. 借书" << endl; cout << "3. 还书" << endl; cout << "4. 查询书籍" << endl; cout << "5. 注销" << endl; cout << "-------------------" << endl; cout << "请选择: ";
if (!(cin >> choice)) { cout << "输入数字无效,请重新输入!" << endl; cin.clear(); cin.ignore(numeric_limits<streamsize>::max(), '\n'); continue; }
auto it = actionMap.find(choice); if (it == actionMap.end()) { cout << "输入数字无效,请重新输入!" << endl; continue; } it->second(); }
return 0; }
|
底层原理
案例代码一
模拟实现 function
类模板的功能,并调用拥有一个参数且不带返回值的函数。
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #include <iostream> #include <functional> #include <string>
using namespace std;
template<typename Fty> class myfunction {
};
template<typename R, typename ARG> class myfunction<R(ARG)> {
public: using PFUNC = R(*)(ARG);
myfunction(PFUNC pfunc) : _pfunc(pfunc) {
}
R operator()(ARG arg) { return _pfunc(arg); }
private: PFUNC _pfunc;
};
void hello(string str) { cout << "hello " << str << endl; }
int main() { myfunction<void(string)> func1 = hello;
func1("peter");
return 0; }
|
程序运行输出的结果如下:
案例代码二
模拟实现 function
类模板的功能,并调用拥有两个参数且带返回值的函数。
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <iostream> #include <functional> #include <string>
using namespace std;
template<typename Fty> class myfunction {
};
template<typename R, typename ARG1, typename ARG2> class myfunction<R(ARG1, ARG2)> {
public: using PFUNC = R(*)(ARG1, ARG2);
myfunction(PFUNC pfunc) : _pfunc(pfunc) {
}
R operator()(ARG1 arg1, ARG2 arg2) { return _pfunc(arg1, arg2); }
private: PFUNC _pfunc;
};
int sum(int a, int b) { return a + b; }
int main() { myfunction<int(int, int)> func1 = sum;
int result = func1(3, 5); cout << "result: " << result << endl;
return 0; }
|
程序运行输出的结果如下:
案例代码三
模拟实现 function
类模板的功能,并调用拥有不同数量参数(可变参数列表)的函数,避免编写多个模板类的特例化。
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| #include <iostream> #include <functional> #include <string>
using namespace std;
template<typename Fty> class myfunction {
};
template<typename R, typename ... ARG> class myfunction<R(ARG...)> {
public: using PFUNC = R(*)(ARG...);
myfunction(PFUNC pfunc) : _pfunc(pfunc) {
}
R operator()(ARG... arg) { return _pfunc(arg...); }
private: PFUNC _pfunc;
};
void hello(string str) { cout << "hello " << str << endl; }
int sum(int a, int b) { return a + b; }
int main() { myfunction<void(string)> func1 = hello;
func1("peter");
myfunction<int(int, int)> func2 = sum;
int result = func2(3, 4); cout << "result: " << result << endl;
return 0; }
|
程序运行输出的结果如下:
C++ 11 的 bind 绑定器
概念介绍
使用案例