/
bilibili 学习网址:https://www.bilibili.com/video/BV1NN4y1F7aY?spm_id_from=333.788.videopod.episodes&vd_source=0fc2f5466dc167b62ed0e5b774c4ab58
C++具有很多的数据类型(基本类型,类类型),在效率和类型安全上是无可比拟的。
但这种语言在很大程度上限制了程序员编写通用代码。
程序员不得不为每一种数据类型编写完全相同或近乎完全相同的代码实现,虽然他们在抽象层面是一致的。
#include
using namespace std;
int max_int(int x, int y) {
return x > y ? x : y;
}
double max_double(double x, double y) {
return x > y ? x : y;
}
string max_string(string x, string y) {
return x > y ? x : y;
}
宏是在预处理阶段针对代码的纯文本替换。
宏本身没有函数的语义(不会对数据类型进行检查)。
虽然可以摆脱类型的约束和限制,但同时也丧失了对数据类型的检查。
# include
using namespace std;
#define Max(x, y) (x>y?x:y)
int main() {
int nx = 10, ny = 20;
double dx = 12.3, dy = 45.6;
string sx = "hello", sy = "world";
// 会被替换成(nx>ny?nx:ny)
cout << Max(nx, ny) << endl;
cout << Max(dx, dy) << endl;
cout << Max(sx, sy) << endl;
// char cx[256] = "world", cy[256] = "hello";
// (cx>cy?cx:cy) 但这只比较了指针的大小,而没有比较字符串
// cout << Max(cx, cy) << endl;
}
通过实例化宏,让预编译器在编译过程中生成相应的函数。
但是代码可读性差。
# include
using namespace std;
// 定义宏函数
#define Max(T) T max_##T(T x, T y) {\
return x > y ? x : y; \
}
// 实例化宏
Max(int)
// int max_int(int x, int y) { return x > y ? x : y; }
Max(double)
// double max_double(double x, double y) {....}
Max(string)
// string max_string(string x, string y) {....}
// 解决代码可读性
# define Max(T) max_##T
int main() {
int nx = 10, ny = 20;
// cout << max_int(nx,ny) << endl;
cout << Max(int)(nx,ny) << endl;
double dx = 12.3, dy = 45.6;
// cout << max_double(dx,dy) << endl;
cout << Max(double)(dx,dy) << endl;
string sx = "world", sy = "hello";
cout << Max(string)(sx,sy) << endl;
// cout << max_string(sx,sy) << endl;
// 类型安全由程序员编写
char cx[256] = "world", cy[256] = "hello";
cout << Max(string)(cx,cy) << endl;
return 0;
}
函数模版的定义形式:
template
返回值类型 函数模版名(调用形参1, 调用形参2, ...) {
.... // 代码段
}
templateT Max(T x, T y) {
return x > y ? x : y;
}
类型形参接受的是类型,调用形参接受的是数据。可以使用任何标识符作为类型形参的名称,但使用“T”已经成为了一种惯例。“T”表示调用者在使用这个函数模版时指定的任意数据类型。
使用函数模版必须对函数模版进行实例化。以下是实例化形式:
# include
using namespace std;
templateT Max(T x, T y) {
return x > y ? x : y;
}
// 函数模版名<类型实参1, 类型实参2, ...>(调用形参1, 调用实参2, ...)
int main() {
int nx = 10, ny = 20;
cout << Max(nx,ny) << endl;
double dx = 12.3, dy = 45.6;
cout << Max(dx,dy) << endl;
string sx = "hello", sy = "world";
cout << Max(sx,sy) << endl;
}
编译器并不是把函数模版编译成一个可以处理任何数据类型的单一实体
编译器在实例化函数模版时根据类型实参从函数模版中产生一个真正的函数实体
函数模版并不是一个函数实体,通过实例化才能产生真正的函数实体
函数模版可以看成是编译器生成函数实体的一个依据而已
这种用具体数据类型替换函数模版类型形参的过程叫做实例化,这个过程将产生一个函数模版的实例(函数实体)
只要使用函数模板,就会自动引发编译器的实例化过程,因此程序员不需要额外地请求对函数模版的实例化
原则上来说可以使用任何类型来实例化函数模版,不管其为数据类型还是类类型
前提是这个类型必须支持函数模版所要执行的操作。
例如:一个不支持“>”操作符的类型来实例化Max函数模版,编译器将报错。
# include
using namespace std;
class Interger{
public:
Interger(int i)m_i(i){}
// 重载Interger类型里面的操作符>
bool operator>(Interger const& that)const{
return m_i>that.m_i;
}
private:
int m_i;
};
templateT Max(T x, T y) {
return x > y ? x : y;
}
int main() {
Interger ix = 100, iy = 200;
// 在没有重载操作符">"之前,编译是会报错的
cout << Max(ix,iy) << endl;
}
编译器对函数模版都会进行两次编译
第一次编译发生在实例化函数模版之前(产生真正函数实体之前),只检查函数模版本身内部代码,查看基本词法是否正确。
函数模版内部出现的所有标识符是否均有声明
已知类型的调用要查看调用是否有效
对于未知类型调用认为都合理
# include
using namespace std;
class A {
public:
void func(){
cout << "A:func()" << endl;
}
};
templateT foo() {
// 标识符未声明,编译不通过
fsfasfasjnf;
// 已知类型调用查看调用是否有效
A a;
a.func();
// cal()未声明,调用无效
a.cal();
// 未知类型的调用都认为合理
T t;
// 合理调用
t.fisjadj();
}
int main() {
return 0;
}
第二次编译发生在实例化函数模版之后(产生真正函数实体之后)结合所使用的类型实参,再次检查模版代码,查看所有调用是否真的均有效。
第一次编译报错,谈不上第二次编译。
# include
using namespace std;
class A {
public:
void func(){
cout << "A:func()" << endl;
}
};
templateT foo() {
// 标识符未声明,编译不通过
fsfasfasjnf;
// 已知类型调用查看调用是否有效
A a;
a.func();
// cal()未声明,调用无效
a.cal();
// 未知类型的调用都认为合理
T t;
// 合理调用
t.fisjadj();
}
int main() {
// 会报错,因为A里面没有声明fisjadj()函数
foo();
return 0;
}
例如:template
例如:Max(123,456)→Max
待续…