函数指针总结

函数指针总结

!! 阅读前提示:
1)本文是自己的学习过程和经验的总结,和大多数人一样,不懂就搜度娘、csdn、博客园等等,内容难免有不足之处和理解不到位的情况,有错误也可以在评论区指出。所有知识来源于网络,摘录内容会指明出处。
2)本文更新于2022/11/15,未完待续,持续更新。

一、普通函数的指针

函数也是有类型的,普通函数的"类型"

返回值 (参数列表)

这就是函数的类型,有些抽象,举个栗子:

int* funCommon(int a, double b)
{
    std::cout<<"funCommon call succeed\n";
    return new int(); 
}

这个函数的类型是

int* (int a, double b)

因此,在程序中可以这样定义

int* (*PTR_FUN )(int,double)=funCommon;

PTR_FUN 就是一个函数指针变量名,定义这个变量的时候并给他初始化了。此时就可以通过这个变量调用funCommon这个函数。

#include <iostream>
int* funCommon(int a, double b)
{
    std::cout<<"funCommon call succeed\n";

    return new int();
    
}
int main()
{
    int* (*PTR_FUN )(int,double)=funCommon;

    PTR_FUN(1,2);

    return 0;
}

编译并执行执行,结果为:

magickiki@magickiki-virtual-machine:~/Projects/vsTest$ g++ -o main main.cpp
magickiki@magickiki-virtual-machine:~/Projects/vsTest$ ./main 
funCommon call succeed
magickiki@magickiki-virtual-machine:~/Projects/vsTest$ 

不过呢,这个样子定义太麻烦了,一般使用typedef或者using进行定义

typedef int* (*PTR_FUN)(int,double);using ptr_fun=int* (*)(int,double);
#include "source.h"

typedef int* (*PTR_FUN)(int,double);

using ptr_fun=int* (*)(int,double);

int main()
{
    PTR_FUN ptr=funCommon;
    ptr_fun ptr2=funCommon;
    ptr(1,2);
    ptr2(1,2);
    return 0;
}

结果是一样的。

magickiki@magickiki-virtual-machine:~/Projects/vsTest$ g++ -o main main.cpp
magickiki@magickiki-virtual-machine:~/Projects/vsTest$ ./main 
funCommon call succeed
funCommon call succeed
magickiki@magickiki-virtual-machine:~/Projects/vsTest$ 

二、类的成员函数指针

现在想通过指针调用类的成员函数,我们先试一下

#include <iostream>
class A
{
    public:
    int* funClass(int a, double b)
    {
    	std::cout<<"funClass call succeed\n";
    	return new int();
    }
    
    static int* staticfunClass(int a, double b)
    {
    	std::cout<<"staticfunClass call succeed\n";
    	return new int();
    }
};
typedef int* (*PTR_FUN)(int,double);
using ptr_fun=int* (*)(int,double);

int main()
{
    PTR_FUN ptr = A::funClass;//此处直接报错,类型不匹配
    PTR_FUN sptr=A::staticfunClass;//这个没报错
    ptr(1,2);
    sptr(1,2);
    return 0;
}

类的静态成员函数的类型同上面普通的函数类型,但是,类的非静态成员函数的类型是,

返回值 (类名::*)(参数列表)

所以想通过指针调用类A的funClass函数,需要定义

typedef int* (A::*class_PTR_FUN)(int,double);
或者
using class_ptr_fun=int* (A::*)(int,double);

代码就变成

#include <iostream>
class A
{
    public:
    int* funClass(int a, double b)
    {
	    std::cout<<"funClass call succeed\n";
    	return new int();
    }
    
    static int* staticfunClass(int a, double b)
    {
    	std::cout<<"staticfunClass call succeed\n";
    	return new int();
    }
};
typedef int* (A::*class_PTR_FUN)(int,double);
using class_ptr_fun=int* (A::*)(int,double);

int main()
{
    class_PTR_FUN ptr=A::funClass;
    PTR_FUN sptr=A::staticfunClass;
    ptr(1,2);//又来一个报错,明显调用的表达式前的括号必须具有(指针)函数类型"
    sptr(1,2);
    return 0;
}

别着急还不能用,成员函数必须通过对象才能调用,
因此要将上面的那句

ptr(1,2)
改为
A a;
(a.*ptr)(1,2);

到此为止,指针讲完了。

说几句闲话,之前遇到了这样一个情况,有一个类A,A有一个类B的成员,在B中处理完需要通知A。
怎么办呢,有这么几个方法。

  • 初始化B类成员时,把A传到B里面。结果:完美解决问题,但是结构耦合了,紧紧的耦合(虽然解决了问题,确实最不好的法子,万一以后要修改这部分代码,耦合的部分就非常容易出问题,就像瞎了的毛线球,除非以后这里一丁点都不会修改)

  • 回调函数,结果:我定义了一个函数指针,还需要A的对象,如果把A传进来,就成上面的问题了。

  • 管道类,专门写一个类,负责传递“消息”。结果:这就是最终解决方法,B把消息给管道类C,C再去调用A。

  • (QT)在Qt中可以使用信号与槽机制

闲话说完了(也就是写这个总结的原因)

补充:类的虚函数的函数指针

(类的虚函数,在学习其他文章时,都不咋建议使用。说真的,类的函数指针,除了静态函数不要对象能够直接调用,其他的都需要对象才能调用,关键你都能拿到对象了,直接调用就好了,再用指针就太多此一举了)

为什么不建议使用虚函数的函数指针呢,因为不确定这个指针会指到哪个函数。
看下面的三个类,A、Dog、Cat,他们都有void call()函数,
在这里插入图片描述
定义为虚函数后,这些虚函数有唯一的一张虚函数表,记录这些虚函数的地址,子类如果重写了基类的虚函数,那么在初始化子类对象时,子类重写的虚函数的地址会覆盖掉虚表中基类的虚函数地址
在这里插入图片描述
Dog和Cat重写了call函数,Dog::call()的地址0x40覆盖了基类的call()函数地址0x20。Cat类也是如此因此,如果子类没有重写这个函数,原有的值不会被覆盖,就像 哑巴 类一样,call()函数的地址值还是原来的0x20.在这里插入图片描述
因此,将一个子类对象赋给基类的指针也能调用子类的成员函数(这就是C++中的多态)
看这几句代码

typedef void (* FUN_PTR)();//定义一个函数指针

void fun(A* pa)
{
	FUN_PTR ptr=A::&call;
	(pa->*ptr)();
}

你能确定调用fun()时,传进来的pa指针是什么吗

可能是fun(new A());
可能是fun(new D());
可能是fun(new Dog());
可能是fun(new Cat());

···全文完

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值