函数指针总结
!! 阅读前提示:
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());
···全文完