Essential C++ (2.7--2.9)

2.7 定义并使用模板函数

当我们发现每个函数的函数体相似,唯一区别在于参数的类型,例如:

void display_message(const string&, const vector<int>& );
void display_message(const string&, const vector<double>& );
void display_message(const string&, const vector<string>& );

将单一的函数内容与希望显示的各种vector类型绑定起来

函数模板(function template):将参数类型抽离出来分为:明确类型(需要改变的),暂缓决定类型(不需要变)

template <typename elemType> // typename是关键词名,elemType是我们需要改变的部分
void display_message(const string &msg, const vector<elemType> &vec){
    // ------
}

这样通过elemtype这个模板,我们可以自主定义不同类型的,而套用同一个函数

模板函数同样可以重载,如果对于display_message()函数,例如list和vector

template <typename elemType> // vector
void display_message(const string &msg, const vector<elemType> &vec){
    // ------
}
template <typename elemType> // list
void display_message(const string &msg, const list<elemType> &lt){
    // ------
}

2.8 函数指针带来更大的弹性

2.8.1 函数指针的定义

首先我们知道:函数名被编译后是一个地址,因此我们也可以用指针取值向他

每一个函数都占用一段内存单元,它们有一个起始地址,指向函数入口地址的指针称为函数指针(函数名)

2.8.2 语法

在定义的时候,函数指针必须指明所指函数的返回类型和参数列表

数据类型 (*指针变量名) (参数表);

// 举例:
int (*p)(int a, int b);  //这里的*p就可以指向max函数 
int max(int a, int b);
2.8.3 说明

注意:

1、函数指针的定义形式中的数据类型是指函数的返回值的类型

注意:指针的数据类型对应函数的返回类型;参数表一一对应

2、区分

int (*p)(int a, int b); //p是一个指向函数的指针变量,所指函数的返回值类型为整型
int *p(int a, int b); //p是函数名,此函数的返回值类型为整型指针

3、 指向函数的指针变量不是固定指向哪一个函数的,而只是表示定义了一个这样类型的变量,它是专门用来存放函数的入口地址的;在程序中把哪一个函数的地址赋给它,它就指向哪一个函数。

4、 在给函数指针变量赋值时,只需给出函数名,而不必给出参数

如函数max的原型为:int max(int x, int y);
指针p的定义为:int (*p)(int a, int b);
则p = max;的作用是将函数max的入口地址赋给指针变量p。这时,p就是指向函数max的指针变量,也就是p和max都指向函数的开头。

5、在一个程序中,指针变量p可以先后指向不同的函数,但一个函数不能赋给一个不一致的函数指针(即不能让一个函数指针指向与其类型不一致的函数

如有如下的函数:int fn1(int x, int y); int fn2(int x);
定义如下的函数指针:int (*p1)(int a, int b); int (*p2)(int a);
则
p1 = fn1; //正确
p2 = fn2; //正确
p1 = fn2; //产生编译错误

6、定义了一个函数指针并让它指向了一个函数后,对函数的调用可以通过函数名``调用,也可以通过函数指针调用(即用指向函数的指针变量调用)。

如语句:c = (*p)(a, b);//表示调用由p指向的函数(max),实参为a,b,函数调用结束后得到的函数值赋给c。

7、 函数指针只能指向函数的入口处,而不可能指向函数中间的某一条指令。不能用*(p+1)来表示函数的下一条指令。

8、 函数指针变量常用的用途之一是把指针作为参数传递到其他函数

2.8.4 实例
int max(int x, int y); //求最大数
int min(int x, int y); //求最小数
int add(int x, int y); //求和
void process(int i, int j, int(*p)(int a, int b)); //应用函数指针
//                             指向函数的指针,对应max,min,add
void process(int i, int j, int(*p)(int a, int b)){
	cout << p(i, j) << endl;
}
int max(int x, int y){
	return x > y ? x : y;
}
int main(){
    int x, y;
    cin >> x >> y;
    process(x, y, max);     // 将函数调用参数进行调用
    return 0;
}

tip:部分借鉴于https://www.jianshu.com/p/405a81d8e7b4

作者:羽裳有涯
链接:https://www.jianshu.com/p/405a81d8e7b4
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.9 设定头文件

1、将函数的声明放在头文件内
// NumSeq.h
bool    seq_elem( int pos, int &elem );
const vector<int> *fibon_seq( int size );
const vector<int> *lucas_seq( int size );
const vector<int> *pell_seq( int size );
// ...
// 这些都是函数的声明,通常不把定义放在头文件内

**注:**关于inline函数:inline函数的定义要写在头文件里

  1. 如果一个inline函数会在多个源文件中被用到,那么必须把它定义在头文件中。
  2. inline是加在实现上,就算加在声明上,编译器也会忽略掉。
  3. 内联展开是在编译时进行的,只有链接的时候源文件之间才有关系。所以内联要想跨源文件必须把实现写在头文件里

备注:

1)如果在头文件中定义非inline函数,一旦该头文件被多个文件包含,就会造成该非inline函数的“重定义”,因而,不建议将非inline函数的定义放在头文件中,但是非inline函数的声明是可以放在头文件中的。

2)inline函数可以省去函数调用的开销,从而提高函数的执行效率,但是如果函数体内代码的执行时间相比于函数调用时间长的多的话,inline函数也就没有什么优势了。

2、注意

在file scope(全局范围)内定义的对象,如果可能被多个文件访问,我们应该将其放在头文件内

3、引用的方法

对头文件引用的时候注意:使用< >和" "

#include <iostream>
#include "NumSeq.h" 
  1. 使用 " " :头文件和包含此文件的程序代码文件位于同一磁盘目录下
  2. 使用 < >: 处于不同磁盘目录下

更专业的回答:如果被认定是标准的或项目专属的头文件,我们便用尖括号,在默认磁盘目录下搜索

如果是引号,则认为是用户提供的,在包含此文件的磁盘

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值