C++ 函数指针详细总结

目录
  • 1、函数指针
    • 1.1 获取函数地址
    • 1.2 声明函数指针
    • 1.3 函数指针传参
    • 1.4 调用函数
  • 2、函数指针进阶

1、函数指针

函数指针顾名思义,就是指向函数的指针。

和数据类似,C++当中函数也有地址,函数的地址是存储函数机器语言代码的内存地址。我们可以将另外一个函数的地址作为参数传入函数,从而实现函数的灵活调用。

1.1 获取函数地址

获取函数地址的方法非常简单,只要使用函数名(后面不跟参数和括号)即可。比如我们有一个函数叫做think,那么think()是调用函数拿到结果,而think则是获取函数的地址。

如果我们想要将think函数当做参数传入另外一个函数,我们可以这么写:

func(think);

1.2 声明函数指针

声明函数指针和声明函数类似,我们声明一个函数可以这么写:

double process(int);

而我们声明函数指针则可以写成这样:

double (*pt)(int);

如果我们把(*pt)替换成函数名的话,这其实就是一个函数的声明。如果(*pt)是函数的话,那么pt自然就是指向函数的指针了。

1.3 函数指针传参

如果我们要实现一个函数,它的一个参数是一个函数指针,它的写法和刚才一样:

double func(double x, double (*pt)(int));

在这个声明当中,它的第二个参数是一个函数指针。指向的函数接收一个int参数,返回一个double结果。

1.4 调用函数

最后, 我们来看下通过指针调用函数的部分。

其实也非常简单,因为我们前面说了(*pt)的效果和函数是一样的,我们之前通过函数名调用函数,那么我们只需要改成通过(*pt)调用即可。

如:

double process(int);
double (*pt)(int);

pt = process;
cout << (*pt)(5) << endl;

2、函数指针进阶

简单的函数指针比较简单,但对于复杂的情况则显得有些恐怖。下面我们来看下C++ primer当中提供的一些例子:

const double* f1(const double ar[], int n);
const double* f2(const double [], int);
const double* f3(const double *, int);

这三个函数看起来长得不一样,但其实是等价的。因为在函数参数列表当中,数组和指针是等价的。其次我们可以在函数的原型中省略掉变量名,因此const double ar[]可以简化成const double [] ,也可以写成const double *。

有了这三个函数之后,假设我们要声明一个指针,指向这三个函数。根据我们前文当中说过的,可以将函数名替换成(*pt)来实现:

const double* (*pt)(const double *, int) = f1;

其实这个语句看起来就有些复杂了,整个语句的可读性很差。如果不是知道这里用的是一个函数指针,乍一看想要看明白估计不太容易。我们可以分成两个部分来理解,其中const double *是一个整体,表示函数的返回值类型是一个const double *也就是一个常量浮点数的地址。其次(*pt)是一个整体,代替了函数名,表示这是一个指向函数的指针。

在C++11当中提供了叫做auto的新特性,它可以帮助变量自动识别对应的类型,可以解决一些类型特别复杂的问题,比如:

auto p2 = f2;

函数指针有两种调用方法,除了可以使用(*p2)的方式调用之外,

也可以直接使用名称调用:

const double* x = p2(ar, 3);
const double* y = (*p2)(ar, 3);

显然前者更好,更清楚。这里其实有一个疑问,为什么这两种方式都可以执行呢?这是因为当我们执行auto p2 = f2的时候,其实是执行的auto p2 = &f2 C++会隐式地将函数转换成函数的地址。因为函数的值本身就是一个地址,所以这两种方式才都能正确地运行。

问题还没有结束,假如我们要定义一个指向函数的指针数组呢?这应该怎么声明?

也就是const double* (*pt)(const double *, int)这样一个类型的数组,它应该怎么声明,这个方括号应该放在那里?

正确答案是放在括号里:

const double* (*pt[3])(const double *, int);

因为运算符[]的优先级高于*,因此*pt[3]表示pt是一个长度为3的指针数组。其他的内容表明了该指针的类型。

由于我们定义的是一个数组,所以这里不能使用auto,因为自动类型推断只能用于单值初始化而不能用于初始化列表。

到这里还没结束,还有更恐怖的,如果我们想要定义一个指向这个数组的指针,应该怎么办呢?如果使用auto可以写成:

auto ptr = &pt;

如果不使用auto呢?首先我们可以想到,这个声明是基于pt的,我们需要在pt的声明上加上一个*,但问题是加在哪里呢?

进一步分析,会发现我们需要指出这是一个指针,而不是数组。意味着核心的部分应该写成(*ptr)[3],表示这是一个指向长度为3的数组的指针。因为[]的优先级更高,所以需要使用括号。如果写成*ptr[3]表示这是长度为3的指针数组。

我们进一步倒推,(*ptr)[3]这个数组当中的元素是什么类型呢?是指向函数的指针,所以写出来结果是这样:

const double *(*(*ptr)[3])(const double*, int) = &pt;

很明显,这样的定义非常非常的难以理解。而且这还不是最复杂的情况,比如函数的返回类型又是一个指向一个函数的指针……明摆着告诉我们含义我们仍然要推敲一会,如果在一段不明的代码当中遇到,可能会直接抓狂吧……

也正因此,C++11当中推出了auto特性,可以简化这种情况。

多说一句题外话,golang语言当中将变量的类型放在变量的后面而不是前面,其中一个原因就是为了解决类似情况的复杂性。

如果是golang来定义同样的内容,会是这样的:

func f2(arr []float64, n int) *float64 {
    // todo
}

// 函数指针
var p1 func([]float64, int) *float64 = f2;
// 函数指针数组
var pt [3]func([]float64, int) *float64;
// 函数指针数组的指针
var ptr *[3]func([]float64, int) *float64 = &pt;

很明显,虽然变量类型写在变量后面刚开始会不太习惯,但是很明显这样要清晰很多。

到此这篇关于C++ 函数指针内容总结的文章就介绍到这了,更多相关C++ 函数指针内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

文章转自公众号:Coder梁(ID:Coder_LT)

(0)

相关推荐

  • C++根据传入的函数指针来解析需要的参数(推荐)

    C++可以根据传入的函数指针,获取自己需要的参数类型,然后根据参数源中获取需要的参数,这里我用tuple作为演示,不过,只要可以根据序号,或者顺序方式等获取实参,都可以使用类似的方式实现: 先给出一个辅助函数: /** 获取第N个类型 */ template <typename... Cases> struct select { }; template <typename T, typename... Cases> struct select<T, Cases...>

  • C++中回调函数及函数指针的实例详解

    C++中回调函数及函数指针的实例详解 如何获取到类中函数指针 实现代码: //A类与B类的定义 class A { public: void Test() { cout << "A::Test()" << endl; } }; class B : public A { public: void Test() { cout << "B::Test()" << endl; } }; //定义类的成员函数指针 typedef

  • c++ 函数指针相关总结

    下面随笔说明函数指针用法. 函数指针的定义: 定义形式: 存储类型 数据类型 (*函数指针名)() 含义: 函数指针指向的是程序代码存储区 函数指针的典型用途-----实现函数回调 通过函数指针调用的函数 例如将函数的指针作为参数传递给一个函数,使得在处理相似事件的时候可以灵活的使用不同的方法. 调用者不关心谁是调用者 需知道存在一个具有特定原型和限制条件的被调用函数. 获取函数指针: 函数的地址就是函数名,要将函数作为参数进行传递,必须传递函数名. 声明函数指针 声明指针时,必须指定指针指向的

  • C++函数指针详解

    函数指针基础: 1. 获取函数的地址 2. 声明一个函数指针 3.使用函数指针来调用函数 获取函数指针: 函数的地址就是函数名,要将函数作为参数进行传递,必须传递函数名. 声明函数指针 声明指针时,必须指定指针指向的数据类型,同样,声明指向函数的指针时,必须指定指针指向的函数类型,这意味着声明应当指定函数的返回类型以及函数的参数列表. 例如: double cal(int); // prototype double (*pf)(int); // 指针pf指向的函数, 输入参数为int,返回值为d

  • C++的函数与指针

    目录 函数与指针 指针作函数的参数-实现地址传递 返回指针的函数 指向函数的指针变量 总结 函数与指针 一个函数在编译时被分配一个入口地址,这个入口地址就称为函数的指针.在C++中, 函数名代表函数的入口地址. 指针作函数的参数-实现地址传递 返回指针的函数 利用指向函数的指针调用函数 指针作函数的参数-实现地址传递 指针作函数的参数,具有以下用途: 指针作函数参数,这时形参接受的是实参的地址.函数中通过对指针的间接访问实现参数的按"引用传递"功能 设置多个指针参数可从函数中带回多个结

  • C++函数指针和回调函数使用解析

    函数指针 函数指针是指向函数的指针变量. 通常我们说的指针变量是指向一个整型变.字符型或数组等变量,而函数指针是指向函数. 函数指针可以像一般函数一样,用于调用函数.传递参数. 函数指针变量的声明: typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数.返回值的函数指针变量 实例 以下实例声明了函数指针变量 p,指向函数 max: #include <stdio.h> int max(int x, int y){ return x > y ? x

  • C++中函数指针详解及代码分享

    函数指针 函数存放在内存的代码区域内,它们同样有地址.如果我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,如同数组的名字就是数组的起始地址. 1.函数指针的定义方式:data_types (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn); c语言函数指针的定义形式:返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,-); c++函数指针的定义形式:返回类型 (类名

  • c++11 符号修饰与函数签名、函数指针、匿名函数、仿函数、std::function与std::bind

    一.符号修饰与函数签名 1.符号修饰 编译器将c++源代码编译成目标文件时,用函数签名的信息对函数名进行改编,形成修饰名.GCC的C++符号修饰方法如下: 1)所有符号都以_z开头 2)名字空间的名字 名字空间(或类)的名字前加上N 名字前还有一个数字,是名字的字符数.比如1C,1是C的长度. 3)函数名 与名字空间一样,函数名前也有数字,比如4func,4是func的字符数. 4)参数 参数以E开头 例子 N::C::func(int) 的函数签名经过修饰为_ZN1N1C4funcEi 2.函

  • C++中指针函数与函数指针的使用

    指针函数 指针函数是一个函数,只不过指针函数返回的类型是某一类型的指针. 格式: 类型名* 函数名(函数参数列表) 使用: /* * 指针函数,返回int* 指针变量 */ int* add(int a, int b) { int *p; int c = a + b; p = &c; return p; } int main() { int* p; p = add(1, 4); printf("%d\n", *p); getchar(); return 1; } 函数指针 函数

  • C++类的空指针调用成员函数的代码

    类的实例调用成员函数的原理 其实不管是通过对象实例或指针实例调用,其实底层调用的过程都是一样的,都是把当前对象的指针作为一个参数传递给被调用的成员函数.通过下面的相关实例代码进行检验: 实验的C++代码 class Student { private: int age; public: Student() {} Student(int age) : age(age) {} int getAge() { return this->age; } }; int main(int argc, char

随机推荐