C++ 函数指针与排序策略
文章目录
- 一、指针函数
- 二、函数指针
- 2.1 定义
- 2.2 函数指针变量的声明
- 2.3 函数指针作为参数
- 2.4 函数的指针类型
- 2.5 函数指针类型作用
- 三、指定排序规则
- 3.1 说明
- 3.2 策略模式 - 对扩展开放,对修改关闭
- 3.3 通用排序算法示例
- 3.4 使用模板实现通用排序
- 3.5 排序算法补充
一、指针函数
本质:是一个函数,其返回值类型为指针(即函数执行后返回一个地址)。
先看下面的函数声明,注意,此函数有返回值,返回值为 int*
,即返回值是指针类型的:
int *f(int a, int b);
上面的函数声明又可以写成如下形式:
int* f(int a, int b);
让指针标志 *
与 int
紧贴在一起,而与函数名 f
间隔开,这样看起来就明了些了:f
是函数名,返回值类型是一个 int
类型的指针。
特点:
- 是函数,不是指针
- 返回值必须是一个合法的指针(避免返回局部变量的地址)
示例代码:
#include <stdio.h>
#include <stdlib.h>int* arrayPrint(int* array, int size)
{int i;for(i = 0; i < size; i++){// *array++先读数据,然后++printf("array[%d] = %d; ", i, *array++);}putchar('\n');// 指针归位for(i = 0; i < size; i++){array--;}return array;
}int main()
{int array[3] = {10, 12, 13};arrayPrint(array, sizeof(array)/sizeof(array[0]));int* array2 = arrayPrint(&array[0], sizeof(array)/sizeof(array[0]));int i;for(i = 0; i < 3; i++){// *array++先读数据,然后++printf("array2[%d] = %d; ", i, *array2++);}putchar('\n');system("pause");return 0;
}
二、函数指针
2.1 定义
与数据项类似,函数也有地址,函数的地址是存储其机器语言代码内存的开始地址;函数指针指向的是函数而非对象。
如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,在这段存储空间的起始地址(又称入口地址)称为这个函数的指针。
- 函数指针是指向函数的指针变量
- 通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数
- 函数指针可以像一般函数一样,用于调用函数、传递参数
- 当我们把函数名作为一个值使用时,该函数自动地转换成指针。还可以直接使用指向函数的指针调用该函数
2.2 函数指针变量的声明
typedef int(*fun ptr)(int, int);
声明一个指向同样参数、返回值的函数指针类型。想要声明一个可以指向函数的指针,只需要用指针替换函数名即可。
示例:
bool lengthCompare(const string& s1, const string& s2); // 函数
bool (*pf)(const string&, const string&); // 函数指针
pf
是一个指向函数的指针,其中该函数的参数是两个 const string
的引用,返回值是布尔类型。
注意:*pf
两端的括号必不可少,如果省略括号,就变成了一个返回值为 bool
指针的函数,而不是指向函数的指针。
示例代码:
#include <stdio.h>
#include <stdlib.h>// 无参数无返回值函数
void printWelcome()
{printf("欢迎来到,矩阵造物!\n");
}// 有参数有返回值函数
int add(int a, int b)
{return a + b;
}int main()
{// 定义函数指针。函数指针是专用的,格式要求很严格void (*p)();// 函数名就是地址,和数组一样p = printWelcome;// 通过指针来调用函数p(); // 直接通过指针名字+()(*p)(); // 取内容// 返回类型(*指针名称)(参数列表);int (*fp1)(int, int);void (*fp2)();// 获取函数的地址fp1 = add;fp2 = printWelcome;printf("Result = %d\n", fp1(10, 9));fp2();return 0;
}
2.3 函数指针作为参数
函数指针可以作为参数传递给其他函数,这在实现回调函数等场景中非常有用。
示例:
#include <iostream>
#include <string>
using namespace std;bool lengthCompare(const string& s1, const string& s2);
void display(const string& s1, const string& s2, bool(*p)(const string&, const string&));int main()
{string name1 = "Sandy";string name2 = "Jane and Sandy";// 定义一个函数指针bool(*pf)(const string&, const string&);// 给函数指针赋值。指针指向lengthCompare函数pf = lengthCompare;// 使用函数指针作为参数display(name1, name2, pf);return 0;
}
2.4 函数的指针类型
函数指针有严格的类型要求,不同类型的函数指针不能随意互换使用。
#include <stdio.h>
#include <stdlib.h>int sum(int number1, int number2)
{int sum = number1 + number2;printf("sum = %d \n", sum);return sum;
}void myPrint()
{printf("Hello World \n");
}int main()
{int (*p1)(int a, int b);int (*p2)(int, int);int (*p3)();void (*p4)();p1 = sum;p2 = sum;p3 = sum;p4 = sum; // 这里会有警告,但可能能编译通过printf("p1 = %d \n", p1(10, 10));printf("p2 = %d \n", p2(100, 100));printf("p3 = %d \n", p3(50, 100));// printf("p4 = %d \n", p4(12, 34)); // 返回值类型报错p4(200, 200); // 可能能运行但结果未定义// 正确使用p4 = myPrint;p4(); // 正确调用return 0;
}
2.5 函数指针类型作用
此时你可能认为函数指针类型没实际作用,但其实函数指针类型就是一个规范,其中从前面的返回值类型也可以看出来,不规定参数,你可以随意传参,甚至可以不传,但是如果规定了参数,你就必须传参。
函数指针类型的主要作用是:
- 类型安全:确保函数指针只能指向具有相同签名的函数,避免错误的函数调用
- 代码规范:明确函数指针所指向函数的参数和返回值类型,提高代码可读性和可维护性
- 回调机制:实现回调函数、策略模式等高级编程技巧
- 动态绑定:在运行时决定调用哪个函数,实现多态性
注意事项:
- 函数指针类型规定了参数和返回值的类型,使用时必须严格遵守
- 不匹配的函数指针赋值可能导致未定义行为
- 使用 typedef 可以简化复杂函数指针类型的声明,提高代码可读性
// 使用typedef简化函数指针类型声明
typedef int (*MathOperation)(int, int);// 现在可以这样声明函数指针变量
MathOperation addPtr = add;
MathOperation subtractPtr = subtract;
三、指定排序规则
3.1 说明
在C++中,函数可以作为另一个函数的参数,这种机制通常通过函数指针来实现。函数指针是指向函数的指针变量,它存储着函数的内存地址,允许我们将函数作为参数传递给其他函数。
不是常量指针不能用 sizeof 去接收,不能在冒泡排序的函数中使用 sizeof 去接收待排序数组的大小
3.2 策略模式 - 对扩展开放,对修改关闭
策略模式是一种设计模式,它允许在运行时选择算法的行为。通过使用函数指针,我们可以将不同的算法(函数)传递给同一个函数,从而实现在不修改原有代码的情况下扩展功能。
#include <iostream>
using namespace std;// 定义几个示例函数
int add(int a, int b) {return a + b;
}int subtract(int a, int b) {return a - b;
}int multiply(int a, int b) {return a * b;
}// 定义一个接受函数作为参数的函数
// 这里的第三个参数是函数指针
int calculate(int a, int b, int (*operation)(int, int)) {return operation(a, b); // 调用传递进来的函数
}int main() {int x = 10, y = 5;// 将不同的函数作为参数传递给calculate函数cout << "加法: " << calculate(x, y, add) << endl;cout << "减法: " << calculate(x, y, subtract) << endl;cout << "乘法: " << calculate(x, y, multiply) << endl;return 0;
}
3.3 通用排序算法示例
我们可以使用函数指针来实现通用的排序算法,允许调用者指定排序规则。
#include <iostream>
using namespace std;struct Dog {int age;int price;Dog() {}Dog(int age, int price) : age(age), price(price) {}
};// 比较函数,根据年龄降序排序
bool compareByAge(Dog a, Dog b) {return a.age > b.age;
}// 比较函数,根据价格降序排序
bool compareByPrice(Dog a, Dog b) {return a.price > b.price;
}// 冒泡排序函数,接受一个函数指针作为比较规则
void BubblingSort(Dog* array, int length, bool (*COMPARE)(Dog a, Dog b)) {for (int i = 0; i < length - 1; i++) {for (int j = 0; j < length - 1 - i; j++) {if (COMPARE(array[j], array[j+1])) {Dog temp = array[j];array[j] = array[j+1];array[j+1] = temp;}}}
}int main() {Dog dogs[5];dogs[0] = Dog(12, 877);dogs[1] = Dog(2, 233);dogs[2] = Dog(8, 456);dogs[3] = Dog(2, 1335);dogs[4] = Dog(4, 12435);// 根据年龄排序BubblingSort(dogs, 5, compareByAge);for (int i = 0; i < 5; i++) {cout << "狗: " << dogs[i].age << " " << dogs[i].price << endl;}// 根据价格排序BubblingSort(dogs, 5, compareByPrice);for (int i = 0; i < 5; i++) {cout << "狗: " << dogs[i].age << " " << dogs[i].price << endl;}return 0;
}
3.4 使用模板实现通用排序
#include <iostream>
using namespace std;// 比较函数模板
template <typename T>
bool compare(T a, T b) {return a > b; // 默认降序
}// 冒泡排序模板
template <typename T>
void BubblingSort(T* array, int length, bool (*COMPARE)(T a, T b)) {for (int i = 0; i < length - 1; i++) {for (int j = 0; j < length - 1 - i; j++) {if (COMPARE(array[j], array[j+1])) {T temp = array[j];array[j] = array[j+1];array[j+1] = temp;}}}
}// 针对Dog结构的比较函数
struct Dog {int age;int price;Dog() {}Dog(int age, int price) : age(age), price(price) {}
};bool compareDogByAge(Dog a, Dog b) {return a.age > b.age;
}int main() {// 对整型数组排序int intArray[] = {12, 23, 45, 78, 53, 98, 450, 7};BubblingSort(intArray, 8, compare<int>);for (int i = 0; i < 8; i++) {cout << intArray[i] << " ";}cout << endl;// 对Dog数组排序Dog dogs[5];dogs[0] = Dog(12, 877);dogs[1] = Dog(2, 233);dogs[2] = Dog(8, 456);dogs[3] = Dog(2, 1335);dogs[4] = Dog(4, 12435);BubblingSort(dogs, 5, compareDogByAge);for (int i = 0; i < 5; i++) {cout << "狗: " << dogs[i].age << " " << dogs[i].price << endl;}return 0;
}
这里 T
是一个占位符,调用时会被具体类型替换。
BubblingSort<int>
会生成针对int
的版本。BubblingSort<Dog>
会生成针对Dog
的版本。
注意事项
- 函数指针的类型必须与所指向函数的类型完全匹配,包括返回类型和参数类型。
- 在传递函数指针时,可以直接使用函数名,因为函数名会自动转换为函数指针。
- 使用函数指针可以实现回调函数,这在事件驱动编程和异步编程中非常常见。
3.5 排序算法补充
#include <stdio.h>
#include <stdlib.h>#define true 1
#define false 0
typedef unsigned char bool;// 定义排序函数类型
typedef void (*SORTTYPE)(int* array, int length, bool (*COMPARE)(int a, int b));// 函数声明
void Sort(int* array, int length, SORTTYPE sortType, bool (*COMPARE)(int a, int b));
void BubblingSort(int* array, int length, bool (*COMPARE)(int a, int b));
void InsertSort(int* array, int length, bool (*COMPARE)(int a, int b));
bool compare(int a, int b);// 比较函数
bool compare(int a, int b) {return a < b ? true : false;
}int main() {// 数据int array[] = {12, 23, 45, 78, 53, 98, 450, 7};int length = sizeof(array) / sizeof(int);// 使用冒泡排序printf("冒泡排序结果: ");Sort(array, length, BubblingSort, compare);for (int i = 0; i < length; i++) {printf("%d ", array[i]);}putchar('\n');// 重置数组int array2[] = {12, 23, 45, 78, 53, 98, 450, 7};// 使用插入排序printf("插入排序结果: ");Sort(array2, length, InsertSort, compare);for (int i = 0; i < length; i++) {printf("%d ", array2[i]);}putchar('\n');return 0;
}// 排序回调函数
void Sort(int* array, int length, SORTTYPE sortType, bool (*COMPARE)(int a, int b)) {sortType(array, length, COMPARE);
}// 冒泡排序实现
void BubblingSort(int* array, int length, bool (*COMPARE)(int a, int b)) {printf("使用冒泡排序: ");for (int i = 0; i < length - 1; i++) {for (int j = 0; j < length - 1 - i; j++) {if (COMPARE(array[j], array[j + 1])) {int temp = array[j];array[j] = array[j + 1];array[j + 1] = temp;}}}
}// 插入排序实现
void InsertSort(int* array, int length, bool (*COMPARE)(int a, int b)) {printf("使用插入排序: ");for (int i = 1; i < length; i++) {int key = array[i];int j = i - 1;while (j >= 0 && COMPARE(array[j], key)) {array[j + 1] = array[j];j--;}array[j + 1] = key;}
}
在 C/C++ 中,当我们把一个数组作为函数参数传递时,数组类型会自动退化为指针类型。在函数内部,arr
只是一个指针变量,它只保存了数组首元素的地址,并不知道有多少个元素。因为函数内部只知道“地址”,不知道“元素个数”,所以必须额外传入长度参数。
例如,在冒泡排序函数中,我们传递了数组的长度length
,而不是在函数内部使用sizeof(array)/sizeof(array[0])
,因为array
实际上是一个指针,而不是数组。
// 错误示例:在函数内部使用sizeof获取数组长度
void BubblingSort(int* array, bool (*COMPARE)(int a, int b)) {int length = sizeof(array) / sizeof(array[0]); // 错误:array是指针,sizeof(array)是指针的大小,而不是数组的大小// ...
}// 正确示例:传递数组长度
void BubblingSort(int* array, int length, bool (*COMPARE)(int a, int b)) {// ...
}