c++模板底层
- 编译器并不是把函数模板处理成能够处理任意类的函数;编译器从函数模板通过具体类型产⽣不同的函数
- 编译器会对函数模板进⾏两次编译:在声明的地⽅对模板代码本身进⾏编译,在 调⽤的地⽅对参数替换后的代码进⾏编译。
- 这是因为函数模板要被实例化后才能成为真正的函数,在使⽤函数模板的源⽂件中包含函数模 板的头⽂件,如果该头⽂件中只有声明,没有定义,那编译器⽆法实例化该模板,最终导致链 接错误。
模板元编程
- 函数名相同,参数类型不同要重新写函数。模板出现就是提高了程序的复用性,提高效率
- 当刚上手的时候肯定是根据具体的数据类型来组织代码。随着越来越熟,用一种广泛的表达去取代具体数据类型,在c++中就叫做模板编程。
类型
- 函数模板
- 类模板
格式
template <template T>
或template <class T>
底层实现
- 编译器将函数模板通过具体类型产生不同的函数
- 对模板代码声明处进行编译
- 在调用地方对替换后代码编译
模板和继承
- 使用目的
- 模板用于生成一组类或函数,这些类和函数的实现是一样的
- 继承是事物之间的联系,从父类到子类是从普遍到特殊,从共性到特性
- 多态的不同
- 模板是编译时多态
- 继承是运行时多态
- 复制内容
- 模板是对代码的复制,编译完成后,会生成对应的函数或类
- 继承是对数据的复制,复制虚表、数据
函数模板
类型
- 成员函数模板
- 普通函数模板
调用方式
- 自动推导,隐式调用
myswap(a, b)
- 参数类型和模板定义的一致才可以
- 模板必须确定出T的类型
- 显式调用
myswap<int>(a, b)
普通函数和模板函数
区别
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 如果使用函数模板,自动类型推导的话,则不会发生隐式转换
- 如果使用函数模板,显式指定类型,则可以发生隐式转换
调用规则
- 优先调用普通函数
- 可以使用空模板参数来强制调用模板函数
- 函数模板也可以重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
类模板
调用方式
只有显式指定参数类型
普通类和模板类
成员函数
- 普通类在编译时创建
- 模板类在调用时创建
类模板对象作函数参数
- 指定传入类型,直接显示对象的数据类型
void print(Person<string, int>& p);
- 参数模板化,将对象中的参数变为模板进行传递
template <class T1, class T2> void print(Person<T1, T2>& p);
- 整个类模板化,将整个对象类型模板化进行传递
template <class T>
void print(T& t);
类模板与继承
- 当派生类继承基类的一个类模板时,子类在声明时,要指定出分类中的T类型
template <class T>
class father{
T t;
};
// 子类在声明时,要指定出分类中的T类型
class son : public father<int>{
}
- 如果还需要灵活,则子类需要变为类模板
template <class T>
class father{
T t;
};
template <class T1, class T2>
class son : public father <T2>{
T1 obj;
}
- 类模板成员的类外实现
// 构造函数类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age){}
// 成员函数类外实现
template<class T1, class T2>
void Person<T1, T2>::show(){}
文件要求
- 要求模板和实现在一个文件内