头等函数
在计算机科学中,如果编程语言允许将函数当作其他任何变量,则该语言提供了头等函数。
一、函数指针
函数指针是一个变量,可储存函数的地址,在程序执行的不同时间可指向不同的函数。
1.1 定义函数指针
示例:
long (*fun_ptr)(long*, int);
上述代码定义了一个函数指针,它可以储存参数为long和int、返回值为long的函数地址。
但要注意的是不能将包括了fun_ptr的圆括号去掉,如果去掉该语句将被解释为返回long指针类型的函数。
如上,函数指针的定义一般形式如下:
return_type (*pointer_name)(parameter_types);
当然,在学了auto以后我们可以很容易想到使用auto去定义函数指针会方便得多:
auto fun_ptr = func_name;
我们也可以添加 * 号来强调变量是指针的事实:
auto* fun_ptr = func_name;
这样,我们就可以将fun_ptr指向任何与type_name拥有一样类型的参数与返回类型的函数了。
以下写法与上述效果等效;
auto fun_ptr = func_name;
auto fun_ptr = &func_name;
auto* fun_ptr = func_name;
auto* fun_ptr = &func_name;
故而很多人建议始终添加取址运算符,因为这样能够增加代码的可读性。
当我们将函数指针指向一个确定的函数只会,我们就可以将其当成和原函数一样的方法取使用,比如:
double myFunc(int val)
{
double result = static_cast<double>(val)*std::numbers::pi;
return result;
}
int main()
{
auto* ptr2 = &myFunc;
std::cout << myFunc(10)<<std::endl; //out:31.4159
std::cout << ptr2(10) << std::endl; //out:31.4159
}
1.2 高阶函数的回调函数
作为实参传递给另一个函数的函数称为“回调函数”(callback function);接收另一个函数作为实参的函数称为“高阶函数”(higher-order function)。
template<typename T>
const T* findOptimum(const std::vector<T>& values, bool (*compare)(const T&, const T&)) //这个函数模板与sort很类型,特别是第二个参数,其作用与sort里的emp参数是一致的
{
if (values.empty()) return nullptr;
const T* optimum = &values[0];
for(size_t i {
1};i<values.size();i++)
{
if (compare(values[i], *optimum))
optimum = &values[i];
}
return optimum;
}
bool less(const int& one,const int& other)
{
return one < other;
}
template<typename T>
bool greater(const T& one, const T& other)
{
return one > other;
}
bool longer(const std::string& one,const std:: string& other)
{
return one.length() > other.length();
}
int main()
{
std::vector<int> numbers{
91,18,92,22,13,43 };
std::cout << "min : " << *findOptimum(numbers, less) << std::endl;
std::cout << "max : " << *findOptimum(numbers, greater) << std::endl;
std::vector<std::string> names{
"Moe","Larry","Shemp","Curly","Joe","Curly Joe" };
std::cout << *findOptimum(names, greater<std::string>) << std::endl;
std::cout << "Longest name : " << *findOptimum(names, longer);
}
输出:
min : 13
max : 92
Shemp
Longest name : Curly Joe
从上面的例子我们就可以看出使用函数指针的好处了:当我们想寻找最大或最小值时,如果我们不适用函数指针传参,那么我们就必须因为比较逻辑的不同而手动分别实现两种函数重载,但是如果我们使用函数指针传参,那么比较逻辑本身就是其中的一个参数,那么我们就不必使用函数重载,这很好的履行了我们之前说所的"DRY"原则。
注意:除了作为高阶函数的实参,头等回调函数还有其他许多用途。在日常的面向对象编程中,也会经常用到回调函数。对象常常在其成员变量中储存回调函数。调用这种回调函数可实现多种用途。它们可作为对象的某个成员函数实现的逻辑中可由用户配置的步骤,也可以用来告诉其他对象发生了某个事件。各种形式的回调函数可辅助各种标准的面向对象技术和模式,最显著的可能是经典Obsever模式的变体。
1.3 函数指针的类型别名
当然,很多时候我们可以使用auto来解决函数