一、类型信息运算符
typeid
在C++中typeid可以获取数据的类型,但是需要加头文件 typeinfo
find /user/inclued -name typeinfo
1、typeid是运算符,执行运算符函数,执行的返回值类型是type_info类类型对象
2、type_info中有个 name 的成员函数
3、type_info中还重载了 == 运算符,可以直接比较两个数据的类型是否相同
4、它可以区分父类指针或引用指向的是哪个子类,但是父子类之间必须构成多态
二、什么是模板
是一种自动生成代码的技术,这种技术能让程序员在编写代码时不需要考虑数据类型,因此也成为泛型编程技术
三、为什么要使用模板
1、C/C++/Java/C# 属于静态编程语言(编写->预处理->编译->汇编->链接->可执行文件)-强类型语言,静态编程语言的优点:运行速度快,缺点:代码改动后重新编译、实现代码的通用比较麻烦
Javascript/python/shell 弱类型语言 动态编程语言
C语言实现一个通用的快排
2、C语言中的通用类型 void* + 回调函数实现的,实现难度大,使用比较麻烦
3、借助定义宏的方式实现代码通用,但是宏有一定的缺点:类型不检查、没有返回值、二义性等问题
4、借助函数重载可以实现函数代码的通用,可能会导致代码段增多,无法解决未知类型
5、综上所述,C++之父为了解决代码通用的问题,实现了模板技术,让C++摆脱数据类型的困扰
四、函数模板
1、函数模板的定义
template<typename T>
void func(T num)
{
}
未知类型名可以取任意名字,一般约定T
2、函数模板的原理
函数模板会经历两次编译
1、检查函数模板的语法是否有错误,如果无误,也不生成函数的二进制指令,代码段没有存储该函数模板
2、根据调用者提供的实参类型再次编译检查函数模板代码,如果也没有错误,才会生成一份二进制指令存储在代码段中,所以如果函数模板没有任何以此调用则不会生成任何二进制指令,如果有不同类型的实参调用函数模板则会生成另一份二进制指令存储在代码段
这种函数模板实例化称为 "惰性实例化" 准则
3、函数模板的调用
C++编译器不会把函数模板当作一个函数的实体,而是当作生成函数实体的工具,当调用函数模板并提供了实际类型参数后才会生成函数实体
调用函数模板必须提供相应数量的参数类型
自动:编译器会自动根据实参的类型,获取函数模板的类型
手动:函数名<type1,type2,...>(实参)会根据<>中提供的类型名去生成函数实体
4、默认形参类型
template<typename T1,typename T2=int,typename T3=long>
T3 函数名(T1 arg1,T2 arg2)
{
T3 ret = arg1 + arg2;
return ret;
}
函数模板的类型参数可以像普通函数的默认形参设置形参值一样去设置默认的形参类型,靠右原则一致,但是该语法只有C++11后才支持
-std=gnu++0x C11语法
5、函数模板的特化(特殊实例化)
模板虽好,但是不能直接解决所有类型的问题,有一些特殊类型的运算规则与普通类型的运算规则不同,例如 char* ,因此需要给这些特殊类型实现一个特殊版本,这种称为函数模板的特化
特化的方式:
1、通过 typeid()分支判断类型执行特殊的步骤
if(typeid(T)==typeid(char*))
{
//实现某些功能
}
2、实现一个特化版本
特化的格式:
template<>
特化类型返回值 函数名(特化类型 形参名){}
注意:
1、特化前,必须有一个基础版本的函数模板
2、template<>一定要加才说明是函数模板的特化
3、特化函数的形参基础类型要与函数模板的形参的基础类型相同,否则报错,例如函数模板中有引用,特化中也需要有
4、编译器会优先调用函数模板中的特化版本,因此不会与基础的函数模板有冲突
5、可以同时存在类型完全相同的普通函数、函数模板特化、函数模板,按照普通->特化->函数模板的顺序调用
6、普通函数无论是否被调用都会生成二进制指令,但是模板特化依然遵循"惰性实例化"准则,不被调用时不生成二进制指令
作业:实现二分查找、冒泡、插入、希尔、快排、归并、堆算法的函数模板
五、类模板
使用未知类型来设计类类型
1、类模板定义
template<typename T1,typename T2>
class 类名
{
T1 成员变量;
public:
T2 func(void)
{
T2 ret;
return ret;
}
};
2、类模板的使用
类模板必须先实例化才能使用,与函数模板不同的是不支持自动实例化,只能显式提供参数手动实例化
类名<类型参数> 对象名
类名<类型参数>* 对象指针 = new 类名<类型参数>(构造函数的实参);
练习:实现链式队列的类模板
3、类模板中的静态成员
类模板中允许有静态成员,与普通类的静态成员变量一样需要在类外定义
template<typename T>
class 类名
{
static 类型名 num;
};
template<typename T> 类型名 类名<T>::num = 初始数据;
对于每个类模板实例化出来的类对象,也是共享静态成员变量
4、递归实例化
什么类型都可以是模板的类型参数,包括类模板类型
template<typename T>
class A
{
T a;
};
template<typename T>
class B
{
T b;
};
B<A<int>> b; //C++11后才允许
5、类模板的默认参数类型
与函数模板的默认参数类型规则一样
template<typename T=类型名>
class A
{
T a;
};
6、类模板的局部特化
当类模板中的成员函数不能支持所有类型时,可以针对不同类型实现类模板中的成员函数的特化版本,称为类模板的局部特化
方法1:通过typeid比较类型,通过分支语句执行特殊操作
方法2:通过实现局部特化成员函数来处理
//局部特化
template<>
int List<const char*>::find(const char* const & data)
{
cout << "局部" << endl;
}
注意:
1、一般在类外实现局部特化
2、类型名的格式除了替换的typename的类型发生替换,其余所有格式都需要一致,例如常属性、引用等
7、类模板的全局特化
为特殊类型重新特化一个类模板,称为类模板的全局特化
template<>
class 类名<特殊类型>
{
重新实现类
}
8、定义类模板和函数模板,class关键字可以替换typename