C+++STL(一)
/
文章目录
- 模版
- C++作为静态类型语言
- 宏可以摆脱数据类型的限制
- 利用宏构建通用函数框架
- 函数模版
- 函数模版的定义
- 函数模版的使用
- 函数模版的分析
- 实例化函数模版的条件
- 函数模版扩展
- 二次编译
- 隐式推断类型实参
- 函数模版的重载
bilibili 学习网址:https://www.bilibili.com/video/BV1NN4y1F7aY?spm_id_from=333.788.videopod.episodes&vd_source=0fc2f5466dc167b62ed0e5b774c4ab58
模版
C++作为静态类型语言
C++具有很多的数据类型(基本类型,类类型),在效率和类型安全上是无可比拟的。
但这种语言在很大程度上限制了程序员编写通用代码。
程序员不得不为每一种数据类型编写完全相同或近乎完全相同的代码实现,虽然他们在抽象层面是一致的。
#include <iostream>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 <iostream>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 <iostream>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<class 类型形参1, class 类型形参2, ...> 返回值类型 函数模版名(调用形参1, 调用形参2, ...) {.... // 代码段 }template<class T>T Max(T x, T y) {return x > y ? x : y; }
类型形参接受的是类型,调用形参接受的是数据。可以使用任何标识符作为类型形参的名称,但使用“T”已经成为了一种惯例。“T”表示调用者在使用这个函数模版时指定的任意数据类型。
函数模版的使用
使用函数模版必须对函数模版进行实例化。以下是实例化形式:
# include <iostream>using namespace std;template<class T>T Max(T x, T y) {return x > y ? x : y;
}
// 函数模版名<类型实参1, 类型实参2, ...>(调用形参1, 调用实参2, ...)
int main() {int nx = 10, ny = 20;cout << Max<int>(nx,ny) << endl;double dx = 12.3, dy = 45.6;cout << Max<double>(dx,dy) << endl;string sx = "hello", sy = "world";cout << Max<string>(sx,sy) << endl;
}
函数模版的分析
-
编译器并不是把函数模版编译成一个可以处理任何数据类型的单一实体
-
编译器在实例化函数模版时根据类型实参从函数模版中产生一个真正的函数实体
-
函数模版并不是一个函数实体,通过实例化才能产生真正的函数实体
-
函数模版可以看成是编译器生成函数实体的一个依据而已
-
这种用具体数据类型替换函数模版类型形参的过程叫做实例化,这个过程将产生一个函数模版的实例(函数实体)
-
只要使用函数模板,就会自动引发编译器的实例化过程,因此程序员不需要额外地请求对函数模版的实例化
实例化函数模版的条件
-
原则上来说可以使用任何类型来实例化函数模版,不管其为数据类型还是类类型
-
前提是这个类型必须支持函数模版所要执行的操作。
例如:一个不支持“>”操作符的类型来实例化Max函数模版,编译器将报错。
# include <iostream>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;
};template<class T>T Max(T x, T y) {return x > y ? x : y;
}int main() {Interger ix = 100, iy = 200;// 在没有重载操作符">"之前,编译是会报错的cout << Max<Interger>(ix,iy) << endl;
}
函数模版扩展
二次编译
编译器对函数模版都会进行两次编译
-
第一次编译发生在实例化函数模版之前(产生真正函数实体之前),只检查函数模版本身内部代码,查看基本词法是否正确。
-
函数模版内部出现的所有标识符是否均有声明
-
已知类型的调用要查看调用是否有效
-
对于未知类型调用认为都合理
# include <iostream>using namespace std;class A { public:void func(){cout << "A:func()" << endl;} };template<class T>T foo() {// 标识符未声明,编译不通过fsfasfasjnf;// 已知类型调用查看调用是否有效A a;a.func();// cal()未声明,调用无效a.cal();// 未知类型的调用都认为合理T t;// 合理调用t.fisjadj(); }int main() {return 0; }
-
-
第二次编译发生在实例化函数模版之后(产生真正函数实体之后)结合所使用的类型实参,再次检查模版代码,查看所有调用是否真的均有效。
-
第一次编译报错,谈不上第二次编译。
# include <iostream>using namespace std;class A {
public:void func(){cout << "A:func()" << endl;}
};template<class T>T foo() {// 标识符未声明,编译不通过fsfasfasjnf;// 已知类型调用查看调用是否有效A a;a.func();// cal()未声明,调用无效a.cal();// 未知类型的调用都认为合理T t;// 合理调用t.fisjadj();
}int main() {// 会报错,因为A里面没有声明fisjadj()函数foo<A>();return 0;
}
隐式推断类型实参
- 如果函数模版的调用形参和类型形参相关。
例如:template<class T>T Max(T x, T y){...}
- 那么在实例化函数模版时即时不显示指明函数模版的类型实参,编译器也有能力根据调用实参的类型隐式推断出正确的类型实参的类型。
例如:Max(123,456)→Max<int>(123,456)
函数模版的重载
待续…