模板
- 概念
- 模板就是建立通用的模具,大大提高复用性
- 模板主要体现了cpp编程的另外一种思想泛型编程
- cpp提供两种模板机制
- 函数模板
- 类模板
- 函数模板
- 作用
- 建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表
- 语法
template<typename T> 函数声明或定义 //template 声明创建模版 //typename 表面其后面的符号是一个数据类型,可以用class代替 //T 通用数据类型,名称可替换
- 使用语法
- 自动类型推导
- mySwap(a, b);
- 显示指定类型
- mySwap(a, b);
- 自动类型推导
- 举例
#include<iostream> using namespace std; template<typename T> void mySwap(T& a, T& b){ T temp = a; a = b; b = temp; } int main(){ int a = 10; int b = 20; double c = 3.54; double d = 4.43; mySwap(a, b); mySwap<double>(c, d); cout << a << b << endl; cout << c << d << endl; return 0; }
- 本质类型参数化
- 注意事项
- 自动类型推导,必须要推导出类型一致的数据类型T才可以使用
- 模版必须要确定出T的数据类型,才可以使用
- class可以用于函数也可以用于类typename只能用于函数
- 普通函数与函数模板的区别
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模版调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示制定类型的方式,可以发生隐式类型转换
- 普通函数和函数模板的调用规则
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以更好的匹配则优先调用函数模板
myfunc<>(a, b)//空模板
- 模板的局限性
- 有些特定数据类型要用具体化方式做特殊实现
#include<iostream> #include<string> using namespace std; class Person{ public: string m_name; int m_age; Person(string name, int age){ m_name = name; m_age = age; } //函数重载是一种方法 // bool operator==(const Person& p){ // if(this->m_age == p.m_age && this->m_name == m_name) // return true; // else // return false; // } }; template<class T> void myCompare(T& a, T& b){ if(a == b){ cout << "same" << endl; }else{ cout << "different" << endl; } } //利用具体化Person的版本实现代码,具体化优先调用 template<>void myCompare(Person& a, Person& b) { if(a.m_age == b.m_age && a.m_name == a.m_name) cout << "same" << endl; else cout << "different" << endl; } void test(void){ Person a("zhangsan",20); Person b("zhangsan", 10); myCompare(a, b); } int main(){ test(); return 0; }
- 作用
- 类模板
- 类模板作用
- 建立一个通用类,类中成员数据类型可以不具体制定,用一个虚拟的类型来表示
- 语法
template<typename T> 类
- 简单举例
#include<iostream> #include<string> using namespace std; template<class NameType, class AgeType> class Person{ public: NameType m_name; AgeType m_age; Person(NameType name, AgeType age){ this->m_age = age; this->m_name = name; } void ShowInfor(void){ cout << "my name is " << this->m_name << endl; cout << "my age is " << this->m_age << endl; } }; void test(void){ Person<string, int>p("zhangsan", 18);//必须显式调用 p.ShowInfor(); } int main(){ test(); return 0; }
- 类模板与函数模板的区别
- 类模板没有自动推导的使用方式
- 类模板在模板参数列表中可以有默认参数
- template<class NameType, class AgeType = int>
- 类模板中成员函数创建时机
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
- 类模板对象做函数参数—如何给函数传入类模板所创造的对象
- 传入方式
- 指定传入类型—直接显示对象的数据类型
- 参数模板化—将对象中的参数变为模板进行传递
- 整个类模板化—将这个对象类型模板化进行传递
- 举例
#include<iostream> #include<string> using namespace std; template<class NameType, class AgeType> class Person{ public: NameType m_name; AgeType m_age; Person(NameType name, AgeType age){ this->m_age = age; this->m_name = name; } void ShowInfor(void){ cout << "my name is " << this->m_name << endl; cout << "my age is " << this->m_age << endl; } }; //指定传入类型---最常用 void Personshow1(Person<string, int> &p){ p.ShowInfor(); } void test1(void){ Person<string, int>p("zhangsan", 18); Personshow1(p); } //参数模板化 template<class T1, class T2> void Personshow2(Person<T1, T2> &p){ p.ShowInfor(); } void test2(void){ Person<string, int>p("zhangsan", 28); Personshow2(p); } // 整个类型模板化 template<class T> void Personshow3(T &p){ p.ShowInfor(); } void test3(void){ Person<string, int>p("zhangsan", 38); Personshow3(p); } int main(){ test1(); test2(); test3(); return 0; }
- 传入方式
- 类模板与继承
- 当子类继承的父类是一个类模板时,子类在声明时候,要指出父类中T的类型
- 如果不指定,编译器无法给子类分配内存
- 如果想灵活指定出父类中T的类型,子类也需变为类模板
- 简单例子
#include<iostream> #include<string> using namespace std; template<class T> class Base{ public: T name; }; //第一种继承方法 class Son1: public Base<string>{ public: void showInfor(void){ cout << "my name is " << this->name << endl; } }; // 第二种方法进一步扩大模板 template<class T1, class T2> class Son2: public Base<T1>{ public: T2 age; void showInfor(void){ cout << "my name is " << this->name << endl; cout << "my age is " << this->age << endl; } }; void test1(void){ Son1 s; s.name = "zhangsan"; s.showInfor(); } void test2(void){ Son2<string, int>s; s.name = "zhangsan"; s.age = 18; s.showInfor(); } int main(){ test1(); test2(); return 0; }
- 类模板中的成员函数的类外实现
- 普通的类外实现
#include<iostream> #include<string> using namespace std; class Person1{ public: void printInfor(void); }; void Person1::printInfor(void){ cout << "this is the test" << endl; } int main(){ Person1 P1; P1.printInfor(); return 0; }
- 普通的类外实现
- 模板的类外实现
#include<iostream> #include<string> using namespace std; template<class T> class Person1{ public: T m_name; Person1(T name); void printInfor(void); }; template<class T>//即使不用T也要添加这个列表 Person1<T>::Person1(T name){ this->m_name = name; } template<class T>//即使不用T也要添加这个列表 void Person1<T>::printInfor(void){ cout << "this is the test" << endl; } int main(){ Person1<string>P1("zhangsan"); P1.printInfor(); return 0; }
- 总结
- 主要是要同步声明其为模板
- 在定义过程的最开始要加入模板列表
- 总结
- 类模板分文件编写
- 问题
- 类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
- 解决
- 直接包含cpp文件
- 一般我们include的都是.h文件,此处可以改为.cpp文件
- 原因
- 模板类不会一开始就生成,因此包含.h并没有什么用
- 将声明和实现写在同一个文件中,并更改后缀名为.hpp,这是约定不是强制,用于实现类模板的实现
- 直接包含cpp文件
- 问题
- 类模板与友元
- 掌握类模板配合友元函数的类内和类外实现
- 全局函数类内实现—直接在类内声明友元即可—推荐—直接加入friend然后定义即可
- 全局函数的类外实现—需要提前让编译器知道全局函数的存在
- 类模板作用