函数指针在C语言中的作用类似于c++中的多态,都是可以实现框架的搭建,代码的兼容性高。
函数三要素:名称、参数、返回值
C语言可以通过typedef为函数类型重命名,语法 typedef 返回值类型(类型名称)(参数列表);如下代码所示:
#include
using namespace std;
int func(int a, int b)
{
return a + b;
}
int main()
{
//函数指针类型定义的第一种方式 : 声明一个 函数类型
typedef int(Func1)(int, int);
Func1 *hello1= func;
cout << hello1(7, 8) << endl;
//函数指针类型定义的第二种方式 : 声明一个函数指针类型
typedef int(*Func2)(int, int);
Func2 hello2 = func;
cout << hello2(7, 8) << endl;
//函数指针定义的第三种方式 : 定义一个指向函数入口地址的函数指针
int(*Func3)(int a, int b);
Func3 = func;
cout << Func3(7, 8) << endl;
system("pause");
return 0;
}
对于编译器来讲,数据类型的本质就是,不同大小的内存块的别名,同样函数也是一种数据类型,那么函数名就是函数的入口地址。函数指针就是向编译器申请类一个类型函数大小的内存,这个块内存存放的是一个函数的入口地址。
我们看函数指针定义的前面两种方式,我们通过typedef对函数类型进行了重命名: typedef int(Func1)(int, int) 就是告诉编译器我现在自定义了一种数据类型 Func1,这个数据类型需要传入两个int型的参数,返回一个int型参数(此时编译器并没有对该数据类型进行世实际的内存分配)。 当 Func1 *hello1= func 这个时候便器器对hello这个指针分配了一个具有Func1特性与大小的内存,并让这个块内存的初始值为func这个函数的入口地址。
当函数指针做函数参数时、传递给一个被调用函数,被调用函数就可以通过这个指针调用外部的函数,这样就形成了回调。
回调函数的本质:提前做了一个协议约定(把函数的参数,返回值提前约定)所有被调用的函数只要符合这个约定,无论函数内部如何实现都是可以的(实现了解耦合)
实例演示:
需求:我们现在要求求出两个数的加减乘三种运算,我们可以先通过约定函数模型,然后进行分工实现,一个人A实现上层的展示与调用,一个人B实现三种运算。
A是实现的部分
#include
using namespace std;
typedef int(*Func)(int, int); //通过函数指针提前约定调用规范。
void player(Func p)
{
cout << p(10, 8) << endl;
}
int main()
{
system("pause");
return 0;
}
B实现的部分:
int func1(int a, int b)
{
return a + b;
}
int func2(int a, int b)
{
return a * b;
}
int func3(int a, int b)
{
return a - b;
}
B需要给A一个myfun.h头文件,里面放了三个函数的声明,和一个func.cpp文件里面放了三个函数的实现。B只需要这两个文件给A,A就可以在实际中将myfun.h头文件声明后对,里面的函数进行调用,完全不需要考虑B如何是实现的。
最终A的代码:
#include
using namespace std;
#include"myfunc.h"
typedef int(*Func)(int, int); //通过函数指针提前约定调用规范。
void player(Func p)
{
cout << p(10, 8) << endl;
}
int main()
{
player(func1);
player(func2);
player(func3);
system("pause");
return 0;
}
这样就是简单实现了代码的解耦合.
所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。
函数模板定义语法:
函数模板调用:
模板函数名<参数数据类型>(参数列表) //显示参数类型调用(强制调用)
模板函数名<>(参数列表) //显示调用(强制调用)
模板函数名(参数列表) //自动数据类型推导(非强制类型调用)
模板函数和普通函数一起时的调用规则:
1 、函数模板可以向普通函数一样被重载
2、C++编译器优先考虑普通函数
3、如果函数木板可以产生一个更好的匹配(普通函数会发生参数类型的强制转换),那么选择模板
3、可以通过空模板实参列表( 模板函数名<>(参数列表) )的语法限定编译器只能通过模板匹配。
4、函数模板不允许自动类型转换(参数类型转换),普通函数能够发生自动类型转换。
编译器并不是把函数模板处理成能够处理任意类的函数,编译器是从函数模板通过具体类型产生不同的函数,编译器会对函数模板进行两次编译:(1)在声明的地方对模板代码本身进行编译;(2) 在调用的地方对替换参数后的代码进行编译
1、模板类中如果使用了构造函数,则遵守以前的类的构造函数的调用规则
2、当子类从模板类继承时,需要指定模板类的数据类型。
3、类模板做函数参数时,需要我们将模板类实例化,因为编译器会对函数进行内存分配(包括参数等),因此需要知道具体的数据类型,只有数据类型固定下来,才可以分配内存。
类模板和函数模板的机制一样都是两次编译,所以不同的模板类接收不同的参数列表之后,生成的类是完全独立的,拥有独立的内内存空间。
有static 关键字定义的成员变量的模板类,每个类模板在确定参数后都会有独自的static关键字的存储单位,之间相互不影响,但是static关键自定义的成员变量的属性在同一个类中的属性不变。
比如有一个类模板
template
class A
{
public:
static int a;
private:
T data;
}
int A::a = 0;
我们使用它实例化参数后生成了两个模板类 A
A a1;
A c1;
所有的A
1、它是类型无关的,具有高度的可复用性
2、它在编译时而不是运行时检查数据类型,保证了类型安全。
3、与平台无关,可移植性。
函数模板与类模板的区别:函数模板的实例化是由编译程序在处理函数调用时自动完成的;而类模板的实例化必须由程序员在程序中显式地指定。