IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++经典问题_09 函数指针 -> 正文阅读

[C++知识库]C++经典问题_09 函数指针

一. 函数的类型和地址

函数也是有类型的,函数的类型由它的返回类型和形参类型共同决定,与函数名无关.
比如下面的函数的类型

bool lengthCompare(const string & a,const string & b);

该函数的类型是bool (const string&,const string&);
和变量一样,函数有固定的地址,直接打印函数名,或者函数的取地址符号,都能打印函数的地址.

/*----------------------------------------------------------------
* 项目: Classical Question
* 作者: Fioman
* 邮箱: geym@hengdingzhineng.com
* 时间: 2022/3/22
* 格言: Talk is cheap,show me the code ^_^
//----------------------------------------------------------------*/

#include <iostream>
using namespace std;
void func(void)
{

}

int main()
{

	cout << "直接通过名称打印地址: " << func << endl;
	cout << "通过地址符号打印地址: " << &func << endl;
	cout << "通过解引用法打印地址: " << *func << endl;
	cout << (func == &func) << endl;
	cout << (&func == *func) << endl;
	system("pause");
	return 0;
}

从上面的例子可以看出来:
func函数名称 和&func以及*func代表的意思都是一样的,都表示的是一个地址,指向保存函数的地址.

二. 函数指针的定义方式

① 函数指针的声明

把函数名替换为指针变量,就是声明一个可以指向该函数的指针.

bool (*pf)(const string&,const string&); // 未初始化

pf指向一个函数,该函数的参数是两个const string的引用,返回值是bool类型

*pf两边的括号不能少,如果不写括号,则pf是一个返回值为bool指针的函数

bool *pf(const string&,const string&);

② 当我们把函数名作为一个值使用时,该函数自动转换为指针

int (*funcPtr)(); // 函数指针的声明
int (*const funcPtr)(); // 指针指向的函数的返回值是常量
/*----------------------------------------------------------------
* 项目: Classical Question
* 作者: Fioman
* 邮箱: geym@hengdingzhineng.com
* 时间: 2022/3/22
* 格言: Talk is cheap,show me the code ^_^
//----------------------------------------------------------------*/

#include <iostream>
using namespace std;
int foo()
{
	cout << "foo() 函数被调用!" << endl;
	return 100;
}

int func()
{
	cout << "func() 函数被调用!" << endl;
	return 200;
}

int main()
{

	// 首先声明一个函数指针
	int (*funcPtr)() = foo; // funcPtr指向了foo;
	funcPtr(); // 可以直接调用
	(*funcPtr)(); // 也可以解引用调用
	//(&funcPtr)(); // 这样调用不行
	funcPtr = func; // 指针指向了func函数,但是不能写成是
	funcPtr();
	(*funcPtr)();

	//funcPtr = func(); // 不能这么调用,这样调用的话funcPtr指向了200这个值了,但是如果func返回的是一个函数指针,也是可以这么调用的
	system("pause");
	return 0;
}

结果:

③ 函数指针在使用的时候注意事项

1. 函数指针声明的形参和返回类型必须和指向的那个函数的形参和返回类型一样,如果不一样就会报错

/*----------------------------------------------------------------
* 项目: Classical Question
* 作者: Fioman
* 邮箱: geym@hengdingzhineng.com
* 时间: 2022/3/22
* 格言: Talk is cheap,show me the code ^_^
//----------------------------------------------------------------*/

#include <iostream>
using namespace std;
int func(int a)
{
	cout << "func() 被调用!" << endl;
	return a;
}

void func_1(int a)
{
	cout << "func_1() 被调用!" << endl;
}



int main()
{
	int (*funcPtr)(int a);
	funcPtr = func; // 正确,类型匹配
	funcPtr(10);
	//funcPtr = func_1; // 错误,返回类型不匹配
	void (*funcPtr1)(int a);
	funcPtr1 = func_1; // 正确,类型匹配
	//funcPtr1 = func; // 错误,类型不匹配
	funcPtr1(10);
	system("pause");
	return 0;
}

2. 重载函数的指针

当我们使用重载函数的时候,上下文必须清晰地界定到底应该选用哪个函数.

void func(int*);
void func(unsigned int);

void (*pf1)(unsigned int) = func; // pf1指向 func(unsigned int);
void (*pf2)(int) = func; // 错误: 没有任何一个func与该形参类型匹配
double (*pf3)(int*) = func; // 错误: pf3的func(int*)的返回类型不匹配.

三. 函数指针形参和返回值

① 形参可以是指向函数的指针,形参看起来是函数类型,实际上却是当成指针来使用

有两种方式声明:

  1. 直接使用函数类型,会自动转换成指向函数的指针
  2. 显示的将形参定义成指向函数的指针
/*----------------------------------------------------------------
* 项目: Classical Question
* 作者: Fioman
* 邮箱: geym@hengdingzhineng.com
* 时间: 2022/3/22
* 格言: Talk is cheap,show me the code ^_^
//----------------------------------------------------------------*/

#include <iostream>
using namespace std;

int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return  a - b;
}

// 形参直接是函数类型,会默认转换为函数指针
void func(int e, int d, int foo(int a, int b))
{
	// 这里的func会直接转换为int (*func)(int a,int b)),函数类型直接转换为函数指针来使用
	cout << "函数调用结果: " << foo(e, d) << endl;
}

// 形参直接是函数指针
void func_02(int e, int d, int (*foo)(int, int))
{
	cout << "函数调用结果: " << foo(e, d) << endl;
}

int main()
{
	func(10, 20, add); // 直接调用加法,形参里面的foo会转换为函数指针
	func_02(10, 20, sub); // 形参里面的foo直接就是函数指针
	system("pause");
	return 0;
}

② 返回指向函数的指针

  1. 首先返回值不能是一个函数,但是可以值返回指向函数类型的指针.
  2. 我们必须要将返回类型写成指针的形式,编译器不会自动地将函数返回类型当成对应的指针类型来处理.
using F = int(int*,int); // F是函数类型,不是指针
using PF = int(*)(int*,int); // PF是指针类型

必须时刻注意: 函数的返回类型必须显示的转换为函数指针类型编译器不会自动进行转换

PF f1(int); // 正确: PF是指向函数的指针,f1返回指向函数的指针
F f1(int); //  错误: F是函数类型,f1不能返回一个函数
F * f1(int); // 正确: 显示的指定f1的返回类型是指向函数的指针

四. 使用typedef 来定义函数类型以及函数指针类型

// Func 和 Func2是函数类型
typedef bool Func(const string&,const string&);
typedef decltype(lengthCompare) Func2;  // 等价的类型

typedef bool(*FuncP)(const string&,const string&);
typedef decltype(lengthCompare) * *FuncP2; // 等价的类型

上例中, FuncFunc2是函数类型,而FuncP以及FuncP2是指针类型.注意decltype()是返回函数的类型,此时不会将函数类型转换为指针类型.

// useBigger的等价声明,其中使用了上面的类型别名
void useBigger(const string&,const string&,Func);
void useBigger(const string&,const string&,FuncP2);
/*----------------------------------------------------------------
* 项目: Classical Question
* 作者: Fioman
* 邮箱: geym@hengdingzhineng.com
* 时间: 2022/3/22
* 格言: Talk is cheap,show me the code ^_^
//----------------------------------------------------------------*/


#include <iostream>
using namespace std;
typedef int (*funcPtr)(int, int); // 定义一个函数指针指针类型是 int (int,int) 返回值为int,参数是int类型的函数指针funcPtr

int add(int a, int b)
{
	return a + b;
}

int sub(int a, int b)
{
	return a - b;
}

int mul(int a, int b)
{
	return  a * b;
}

int divison(int a, int b)
{
	return b ? a / b : -1;
}

// 定义一个函数,参数是op,返回一个指针,该指针类型为拥有两个int参数,返回类型是int的函数指针.
funcPtr calc_func(char op)
{
	switch (op)
	{
	case '+':
		return add;
	case '-':
		return sub;
	case '*':
		return mul;
	case '/':
		return divison;
	default:
		return NULL;
	}
}

// 直接定义一个函数指针.
int (*s_calc_func(char op)) (int, int)
{
	return calc_func(op);
}

int calc(int a, int b, char op)
{
	funcPtr fp = calc_func(op);
	int (*s_fp)(int, int) = s_calc_func(op); // 和上面的等价
	cout << "fp: " << fp << ",s_fp: " << s_fp << endl;
	return fp(a, b);
}

int main()
{
	int a = 100, b = 200;
	cout << a << '+' << b << "=" << calc(a, b, '+') << endl;
	cout << a << '-' << b << '=' << calc(a, b, '-') << endl;
	cout << a << "*" << b << '=' << calc(a, b, '*') << endl;
	cout << a << "/" << b << "=" << calc(a, b, '/') << endl;

	system("pause");
	return 0;
}

结果:

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 23:08:08  更:2022-04-01 23:09:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:55:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码