C++复习笔记(六)之函数指针和函数模板、类模板

一、函数指针

函数指针在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;
}

这样就是简单实现了代码的解耦合.

四、函数模板

所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。

函数模板定义语法:

C++复习笔记(六)之函数指针和函数模板、类模板_第1张图片

 

函数模板调用:

                       模板函数名<参数数据类型>(参数列表)    //显示参数类型调用(强制调用)

                       模板函数名<>(参数列表)                          //显示调用(强制调用)

                       模板函数名(参数列表)                              //自动数据类型推导(非强制类型调用)

函数重载与模板函数

模板函数和普通函数一起时的调用规则:

1 、函数模板可以向普通函数一样被重载

2、C++编译器优先考虑普通函数

3、如果函数木板可以产生一个更好的匹配(普通函数会发生参数类型的强制转换),那么选择模板

3、可以通过空模板实参列表(   模板函数名<>(参数列表)   )的语法限定编译器只能通过模板匹配。

4、函数模板不允许自动类型转换(参数类型转换),普通函数能够发生自动类型转换。

函数模板机制结论

编译器并不是把函数模板处理成能够处理任意类的函数,编译器是从函数模板通过具体类型产生不同的函数,编译器会对函数模板进行两次编译:(1)在声明的地方对模板代码本身进行编译;(2) 在调用的地方对替换参数后的代码进行编译

五、类模板

C++复习笔记(六)之函数指针和函数模板、类模板_第2张图片

1、模板类中如果使用了构造函数,则遵守以前的类的构造函数的调用规则

2、当子类从模板类继承时,需要指定模板类的数据类型。

3、类模板做函数参数时,需要我们将模板类实例化,因为编译器会对函数进行内存分配(包括参数等),因此需要知道具体的数据类型,只有数据类型固定下来,才可以分配内存。

类模板和函数模板的机制一样都是两次编译,所以不同的模板类接收不同的参数列表之后,生成的类是完全独立的,拥有独立的内内存空间。

有static 关键字定义的成员变量的模板类,每个类模板在确定参数后都会有独自的static关键字的存储单位,之间相互不影响,但是static关键自定义的成员变量的属性在同一个类中的属性不变。

比如有一个类模板

template  
class A
{
public:
   static int a;
private:
   T data;
}
int A::a = 0;

我们使用它实例化参数后生成了两个模板类 A 、A 并创建了 两个不同类型的对象a1,c1;

A a1;
A c1;

所有的A 模板类创建的对象共同使用同一块内存存放static关键字定义的成员变量;所有的A 模板类创建的对象共同使用同一块内存存放static关键字定义的成员变量,两个互不相同。

六、类模板的好处

1、它是类型无关的,具有高度的可复用性

2、它在编译时而不是运行时检查数据类型,保证了类型安全。

3、与平台无关,可移植性。

  函数模板与类模板的区别:函数模板的实例化是由编译程序在处理函数调用时自动完成的;而类模板的实例化必须由程序员在程序中显式地指定。

你可能感兴趣的:(C/C++复习笔记)