在 C++ 中,所有可以像函数一样被调用的实体,统称为可调用对象(Callable Objects)。这些对象不仅仅局限于普通函数,还包括类的静态成员函数、仿函数(Functors)、lambda
表达式、类的非静态成员函数以及可转换为函数指针的类对象。本文将详细介绍这些不同的可调用对象,并探讨它们的使用场景及特点。嗯,语气比较严肃。!_!
一、普通函数#
普通函数是最常见的可调用对象。在 C++ 中,普通函数可以通过函数指针和函数引用来存储和调用。
1.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
26
27
|
#include <iostream>
using namespace std;
using Fun = void (int, const string&); // 定义普通函数类型的别名
// 函数声明
Fun show;
int main()
{
show(1, "我是普通函数调用。");
void(*fp1)(int, const string&) = show; // 函数指针
void(&fr1)(int, const string&) = show; // 函数引用
fp1(2, "使用函数指针调用。");
fr1(3, "使用函数引用调用。");
Fun* fp2 = show; // 使用别名创建函数指针
Fun& fr2 = show; // 使用别名创建函数引用
fp2(4, "使用别名的函数指针。");
fr2(5, "使用别名的函数引用。");
}
// 函数定义
void show(int bh, const string& message) {
cout << "编号" << bh << ":" << message << endl;
}
|
1.2 注意事项#
- 普通函数可以通过函数指针和函数引用来调用。
- 函数的类型可以通过
using
来定义别名,以简化代码。
- C++ 中不能用函数类型定义函数的实体,函数类型只能用于声明。
二、类的静态成员函数#
类的静态成员函数与普通函数本质上相同。它们与类的实例无关,因此可以直接通过类名或函数指针进行调用。
2.1 静态成员函数的定义与调用#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <iostream>
using namespace std;
struct MyClass {
static void show(int bh, const string& message) {
cout << "编号" << bh << ":" << message << endl;
}
};
int main() {
MyClass::show(1, "直接调用静态成员函数。");
void(*fp1)(int, const string&) = MyClass::show; // 函数指针指向静态成员函数
fp1(2, "使用函数指针调用静态成员函数。");
using Fun = void (int, const string&);
Fun* fp2 = MyClass::show;
fp2(3, "使用别名的函数指针调用静态成员函数。");
}
|
2.2 特点#
- 静态成员函数不依赖于对象实例,可以像普通函数一样调用。
- 与普通函数类似,静态成员函数也可以使用函数指针和函数引用来存储和调用。
三、仿函数(Functor)#
仿函数本质上是一个重载了 operator()
运算符的类。通过这种运算符重载,类的对象可以像函数一样被调用。
3.1 仿函数的定义与调用#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <iostream>
using namespace std;
struct Functor {
void operator()(int bh, const string& message) {
cout << "编号" << bh << ":" << message << endl;
}
};
int main() {
Functor fun;
fun(1, "调用仿函数。");
Functor()(2, "调用匿名对象的仿函数。");
}
|
3.2 特点#
- 仿函数是通过
operator()
运算符来模拟函数调用的类对象。
- 仿函数常用于 STL 算法中,它们可以存储状态,比普通函数更灵活。
四、Lambda表达式#
Lambda 表达式本质上是一个匿名的仿函数。它是 C++11 引入的一个特性,用于简洁地定义内联函数。
4.1 Lambda表达式的定义与调用#
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <iostream>
using namespace std;
int main() {
auto lambda = [](int bh, const string& message) {
cout << "编号" << bh << ":" << message << endl;
};
lambda(1, "调用Lambda表达式。");
auto& lambda_ref = lambda; // 引用Lambda表达式
lambda_ref(2, "引用Lambda表达式调用。");
}
|
4.2 特点#
- Lambda 表达式是一种简洁的匿名函数对象,通常用于一次性操作。
- Lambda 表达式的类型是匿名的,因此只能通过
auto
或模板进行类型推导。
五、类的非静态成员函数#
类的非静态成员函数不同于普通函数和静态成员函数,它只能通过类的实例进行调用。其指针类型特殊,不能通过引用调用。
5.1 非静态成员函数的定义与调用#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <iostream>
using namespace std;
struct MyClass {
void show(int bh, const string& message) {
cout << "编号" << bh << ":" << message << endl;
}
};
int main() {
MyClass obj;
obj.show(1, "直接调用非静态成员函数。");
void (MyClass::* fp)(int, const string&) = &MyClass::show;
(obj.*fp)(2, "通过成员函数指针调用。");
}
|
对于普通函数,你可以直接写函数名来获取它的地址,但对于成员函数,由于它依赖于类和对象,必须通过 &
明确表示你是在获取地址,而不是在引用或声明函数。
5.2 特点#
- 非静态成员函数必须通过对象调用,不能像普通函数一样通过函数指针直接调用。
- 只能使用成员函数指针来调用非静态成员函数。
六、可转换为函数指针的类对象#
通过重载 operator()
或重载类型转换运算符,类对象可以被转换为函数指针,使得类的对象也能像函数一样调用。这个,我认为仅作了解吧,我不是很理解。
6.1 可转换为函数指针的类对象#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include <iostream>
using namespace std;
void show(int bh, const string& message) {
cout << "编号" << bh << ":" << message << endl;
}
struct CallableClass {
using Fun = void(*)(int, const string&);
operator Fun() {
return show;
}
};
int main() {
CallableClass callable;
callable(1, "调用可转换为函数指针的类对象。");
}
|
6.2 特点#
- 这种方式允许将类对象隐式转换为函数指针,调用方式类似函数对象。
- 在实际开发中,这种技巧较为少见,但可以用于某些特殊需求场景。
C++中的可调用对象种类丰富且灵活,包括普通函数、静态成员函数、仿函数、lambda
表达式、非静态成员函数以及可转换为函数指针的类对象。每种可调用对象都有其特定的使用场景与优点,在编写泛型代码、设计类接口以及高效处理函数调用时,这些可调用对象能够极大地提升代码的灵活性与可扩展性。