普通函数指针
- 函数指针的定义方式
return_type (*name)(arg1, arg2,...., agrn)
函数指针的组成和函数签声明定义无多大规则,仅仅只需要在函数名字签名加上*, [返回值+(*函数指针名字)+(参数列表)]
其中括号是必须的。
2. 函数指针的赋值及调用
函数指针在使用前是必须赋值的,赋值就很容易了,只需要知道函数名字和数组名字一样,都代表首地址,即函数名字代表函数的首地址,数组名字代表数组首地址。调用函数时候仅仅使用() 操作符就可以调用函数了,和使用函数名字调用函数大同小异
```cpp
#include <iostream>
void foo() // 定义无参无返回值函数
{
std::cout << "call foo function." << std::endl;
}
int add(int a, int b)
{
return a + b;
}
int main()
{
void (*pfoo)(); // 定义名字为fp的函数指针
pfoo = foo; // 对函数指针赋值 函数名字即是函数指针的地址
pfoo(); // 调用函数指针 使用括号操作符
int (*padd)(int, int); // 两个参数函数指针定义
int (*padd_arg_name)(int a, int b); // 也可以写成 参数名字可有可无
padd = add; // 赋值
padd_arg_name = add;
int ret = padd(4, 5); // 调用
int ret_arg_name = padd_arg_name(4, 5);
return 0;
}
```
- 使用typedef简化函数指针定义
typedef 是c++ 提供的关键字,重定义别名, typedef typename 就是 重定义一种还没有确定的类型的别名。typedef T U, 就是把T这种类型重定义别名为U。
例如:
函数指针也是一种类型,可以理解为无参的函数指针,一个参数的,两个参数的,参数类型又可以不相同,就可以组成无穷无尽的函数指针,并将某个函数指针使用typedef 简化后,更方便阅读和理解。可以举个四个参数的函数指针。typedef char byte; // 将char 使用别名byte代替 typedef int int32_t; // 将int 使用别名int32_t代替
这里使用的是typedef 的特殊用法,不像常规用法那样 typedef 老变量名 新变量名。int (*draw_rect)(int x, int y, int width, int height); // 指向绘制一个矩形的函数指针 typedef int (*draw_rect)(int x, int y, int width, int height);
typedef 的特殊用法用三种: 即数组别名, struct 别名, 函数指针别名。- 数组别名
数组这种特殊的别名就是数组的名字。typedef int Point[2]; // 将int [2] 这种类型当做别名Point Point point; int x = 1, y = 2; point[0] = x; point[1] = y; std::cout << point[0] << " " << point[1] << std::endl;
其实定义别名的时候去掉typedf和别名, 剩下的就是原来变量的类型。
typedef int Point[2] 去掉typedef 和 Point,剩下int[2] ,即int 类型的两个元素数组。- 结构体别名
typedef struct { int x; int y; } Point; // 去掉typedef 和Point 剩下的是一个结构体 typedef struct Point_ { } Pt;
- 函数指针别名
函数指针和数组有莫大关系,比如名字代表首地址。那么别名规则也是一样的,
使用typedef别名之后,理解函数指针变量就很轻松了,本来函数指针长void(*fp)()这个样子。那么我想定义多个这种变量只能这样写。// 去掉别名和typedef 代标的类型是int(*)(int x, int y, int width, int height) 妥妥一个函数指针的样子 typedef int (*draw_rect)(int x, int y, int width, int height);
这种方式对于代码阅读和书写总不友好,哪有变量名字写在中间的,一般不都是类型+ 变量名这种书写习惯,所以使用tyepdef后void(*fp)(); void(*fp_1)(); void(*fp_2)(); // 赋值 函数名字即地址 fp = foo; fp_1 = foo; fp_2 = foo;
typedef void(*fp)(); fp p1; // 此种方式就是很好理解了,符合大多数人的习惯。 fp p2; fp p3; // 赋值 p1 = foo; p2 = foo; p3 = foo;
- 函数指针数组
数组就是存储相同的类型,既然函数指针是一种类型,那么肯定是可以使用数组存储的。#include <iostream> int add(int a, int b) { std::cout << "call add." << std::endl; return a + b; } int sub(int a, int b) { std::cout << "call sub." << std::endl; return a - b; } int main() { typedef int (*Fun)(int a, int b); // 同时表示加减法 // 函数指针别名数组 Fun add_sub[2] = { 0 }; // 20个元素全部初始化为nullptr add_sub[0] = add; add_sub[1] = sub; for (int i = 0; i < 20; i++) { int index = i % 2; // 循环调用数组中第一个元素和第二个元素 add_sub[index](i, i); } std::cout << "奇怪的写法" << std::endl; int (*pFun[2])(int a, int b); // 这种写法很奇怪,一般不容易理解 pFun[0] = sub; pFun[1] = add; for (int i = 0; i < 20; i++) { int index = i % 2; // 循环调用数组中第一个元素和第二个元素 pFun[index](i, i); } return 0; }
类成员函数指针
类静态函数指针
- 静态函数指针的定义方式
定义方式与普通函数定义方式大同小异,仅仅是赋值的时候需要使用&类名:函数名#include <iostream> class Foo { public: static void foo() { std::cout << "call static funciton foo." << std::endl; } }; int main() { void (*pf)(); // 与普通函数指针无区别 pf = &Foo::foo; // 赋值需要使用取地址符 pf(); // 调用与普通指针无区别 return 0; }
- 静态函数指针与普通指针的区别
唯一的区别就是需要使用&类名:函数名获取函数地址,类的静态成员函数不在代表函数指针的首地址。
类成员函数指针
- 定义
类成员函数指针的定义仅仅需要加上类的命名空间如下:// [返回值](类名::*函数指针名字)(参数列表) return_type (className::*pf)(arg list)
- 赋值
赋值与函数指针的区别是使用&类名::函数名获取地址。 - 调用
调用分为两条:- 是一个对象实例调用:.* (类对象.*指针名)(参数列表) 其中两个()是必须要的,第一个括号代表优先级,第二个括号代码调用
- 是一个对象指针: ->* (类指针->*指针名)(参数列表) 其中两个()是必须要的,第一个括号代表优先级,第二个括号代码
- 示列代码
#include <iostream> class Foo { public: static void foo() { std::cout << "call static funciton foo." << std::endl; } int add(int a, int b) { std::cout << "call class add ." << std::endl; return a + b; } }; int main() { int (Foo::*pAdd)(int, int); pAdd = &Foo::add; //pAdd(1, 2); 错误,需要使用类对象或者类指针调用 Foo f; Foo* p = new Foo; // f.*pAdd(1, 2); 错误.的优先级太低,需要使用括号获取指针才能调用 (f.*pAdd)(1, 2); // 正确 (p->*pAdd)(1, 2); typedef int (Foo::* pf)(int, int); // 使用typef pf padd = &Foo::add; // padd(1, 2); 错误,需要使用类对象或者类指针调用 (f.*padd)(1, 2); // 对象 (p->*padd)(1, 2); // 指针 return 0; }