7.5 内联函数
函数可以对代码进行封装和方便重复使用,但是对代码进行调用需要额外付出一定的性能代价,需要记录当前位置然后跳转到要调用的函数,假如只有几行代码的函数,那其实不如直接将代码复制过去的运行效率高。
所以为了进一步挖掘运行的效率,对于代码数不多的函数,可以将其代码复制过去,这种操作叫内联函数。关键字为inline
。
inline int min(int x, int y)
{
return x > y ? y : x;
}
但实际上,现在的编译器会自动实现这个功能,所以不需要专门去进行这项操作。
7.6 函数重载
函数重载是个很重要的概念,一方面它可以方便函数的使用,另一方面它也是C++作为面向对象编程的“多态”特性的关键,本节先介绍它对函数使用的扩展。
因为C的函数需要限定函数的类型和输入参数的类型,所以便有下面的一个困境:
int addInteger(int x, int y)
{
return x + y;
}
double addDouble(double x, double y)
{
return x + y;
}
完全相同的两个函数,就是因为类型不一样,就得定义两个函数,这在调用的时候会比较麻烦,这是显然可以提高效率的地方,所以可以针对功能一样的函数,定义函数名一样、类型不一样的函数,进行函数重载,比如上面的函数可以定义为:
int add(int x, int y)
{
return x + y;
}
double add(double x, double y)
{
return x + y;
}
这并不会带来函数命名冲突的编译错误,编译器会自动匹配到合适的函数。
函数重载不仅可以定义不同的数据类型,如果是不同的函数个数也可以,比如也可以在定义一个:
int add(int x, int y, int z)
{
return x + y + z;
}
但是要注意,函数的参数必须使函数能够唯一被选择,下面的定义是非法的:
void printValues(int x);
void printValues(int x, int y=20);
因为printValues(10)
对于上面两个的定义都是符合的,编译器将会不知道匹配到哪里。
7.7 函数的默认参数
C++的函数默认参数跟python基本一致,有几个注意点总结一下:
- 默认参数中最不可被修改放到最右侧,需要传入的参数放到左侧,下面的定义是非法:
void printValue(int x=10, int y); // not allowed
- 如果有多个默认参数,不能跳过左侧的参数去修改右边的默认参数,比如:
void printValue(int x, int y=10);
printValue(,20) //Error!!
不能跨过左边的参数去给右边的参数赋值。
- 默认参数只能声明一次
在头文件中的函数声明和cpp文件里函数的定义,这两者中默认参数只能被声明一次,建议在头文件的声明中定义默认参数,这样可以被其他文件看到。
比如:在foo.h中:
#ifndef FOO_H
#define FOO_H
void printValues(int x, int y=10);
#endif
在main.cpp中:
#include "foo.h"
#include <iostream>
void printValues(int x, int y)
{
std::cout << "x: " << x << '\n';
std::cout << "y: " << y << '\n';
}
int main()
{
printValues(5);
return 0;
}
7.8 函数指针
变量是存储在内存中,变量指针指向变量的存储地址。
同理,函数存储也是存储在内存中,所以函数肯定也有指针。
#include <iostream>
int foo() // code starts at memory address 0x002717f0
{
return 5;
}
int main()
{
std::cout << foo; // we meant to call foo(), but instead we're printing foo itself!
return 0;
}
输出:
0x002717f0
对于上段代码,能发现foo
就指函数的地址,代指函数指针。
函数指针的一大作用就是将函数当成参数可以供函数使用。
跟变量指针需要定义和声明一样,函数指针也需要声明,由于每种函数都定义的千差万别,所以指针前的类型不能简单用int
、float
这些代替,有几种定义方法, 这里直接推荐C++11以后使用std :: function
的一种方式:
#include <functional>
std::function<bool(int, int)> fcn
上述定义中括号内分别写上函数的类型,函数参数的类型,后面跟上函数指针的变量名。
#include <functional>
#include <iostream>
int foo()
{
return 5;
}
int main()
{
std::function<int()> fcnPtr ; // declare function pointer that returns an int and takes no parameters
fcnPtr = foo; // fcnPtr now points to function goo
std::cout << fcnPtr(); // call the function just like normal
return 0;
}
这是一个简单的例子,下面列举一个可以当作函数参数的例子。
对于数组排序函数,有时候需要升序,或降序,把升序或者降序的判断函数当作参数传给排序函数。
#include <utility> // for std::swap
#include <iostream>
#include <functional>
// Note our user-defined comparison is the third parameter
void selectionSort(int *array, int size, std::function<bool(int,int)> comparisonFcn)
{
for (int startIndex = 0; startIndex < size; ++startIndex)
{
int bestIndex = startIndex;
for (int currentIndex = startIndex + 1; currentIndex < size; ++currentIndex)
{
if (comparisonFcn(array[bestIndex], array[currentIndex]))
bestIndex = currentIndex;
}
std::swap(array[startIndex], array[bestIndex]);
}
}
// Here is a comparison function that sorts in ascending order
bool ascending(int x, int y)
{
return x > y;
}
// Here is a comparison function that sorts in descending order
bool descending(int x, int y)
{
return x < y;
}
// This function prints out the values in the array
void printArray(int *array, int size)
{
for (int index=0; index < size; ++index)
std::cout << array[index] << " ";
std::cout << '\n';
}
int main()
{
int array[9] = { 3, 7, 9, 5, 6, 1, 8, 2, 4 };
selectionSort(array, 9, descending);
printArray(array, 9);
selectionSort(array, 9, ascending);
printArray(array, 9);
return 0;
}