C++函数模板&类模板
函数模板
模板概念及语法
主要目的,简化代码,减少重复代码。基本语法格式: template<class T> 或者 template<typename T> //末尾不加分号
1 template <class T> //等价于 template <typename T> 2 3 void mySwap(T &a, T &b){ 4 T tmp = b; 5 b = a; 6 a = tmp; 7 } 8 9 void test02(){ 10 int a = 10, b = 20; 11 //自动推导类型,里面必须有参数,而且参数类型必须相同,因为两者是同一个类型T,这也是类型参数化的意义 12 mySwap(a, b); 13 //显式调用,显式指定类型 14 mySwap<int>(a, b); 15 16 cout << "a = " << a << "b = " << b << endl; 17 }
函数模板与普通函数
1.与类模板区别
template声明下面是函数定义,则为函数模板,否则为类模板。注意:每个函数模板前必须有且仅有一个template声明,不允许多个template声明后只有一个函数模板,也不允许一个template声明后有多个函数模板。
2. 与普通函数的区别
(1)函数模板不可以进行隐式类型转换,普通函数可以进行隐式类型转换;
(2)两者的调用规则:
①若函数模板和普通函数出现了重载(两者仅仅参数类型不同,一个为T,一个为具体类型),优先使用普通函数调用,若普通函数没有实现,则会出现“无法解析命令”的错误,不会因此而调用函数模板;
②如果想强制调用模板,可以使用空函数列表
③函数模板可以发生重载(参数类型和个数等)
④如果函数模板可以更好的匹配,优先调用函数模板,如果函数模板和普通函数都能匹配,优先调用普通函数(这一点和①类似)。
3. 模板机制剖析
(1)模板不是万能的,不能处理所有的数据;
(2)函数模板不可以直接调用,需要编译器处理成模板函数后才能调用;
(3)编译器对函数模板进行两次编译,一次是在声明的地方对模板代码(函数模板)进行编译,一次对调用时对参数替换后的代码(模板函数)进行编译。
1 template <class T> 2 bool myComp(T &a, T &b){ 3 4 cout << "模板函数调用" << endl; 5 if (a == b) 6 return true; 7 else 8 return false; 9 } 10 11 //函数模板重载 12 template <class T> 13 bool myComp(T &a, T &b, T &c){ 14 15 cout << "模板函数调用" << endl; 16 if (a == b == c) 17 return true; 18 else 19 return false; 20 } 21 22 ////如果int类型的参数调用上述函数模板,会替换参数类型处理为模板函数如下 23 //bool myComp(int &a, int &b){ 24 // if (a == b) 25 // return true; 26 // else 27 // return false; 28 //} 29 30 bool myComp(int a, int b){ 31 32 cout << "普通函数调用" << endl; 33 34 if (a == b) 35 return true; 36 else 37 return false; 38 } 39 40 void test02(){ 41 int a = 10, b = 20, c = 40; 42 float d = 40; 43 44 //普通函数和模板函数都可以匹配,优先调用普通函数 45 myComp(a, b); 46 //普通函数中形参可以隐式转换(隐式强转),函数模板不可以 47 myComp(a, d); 48 }
4. 函数模板的局限性
(1)模板不能解决所有数据类型;
(2)如果出现不能解决的类型,可以通过数据类型具体化来满足要求;
(3)数据类型具体化语法: template <> 返回值 函数名 <具体类型> (参数) ,将数据类型具体化,仅仅是数据类型,不能修改返回值类型和函数名。
(4)当具体化和参数化同时匹配时,优先使用具体化
1 class Person{ 2 public: 3 Person(string name, int age){ 4 m_Name = name; 5 m_Age = age; 6 } 7 8 string m_Name; 9 int m_Age; 10 11 }; 12 13 template <class T> 14 bool myComp(T &a, T &b){ 15 if (a == b) 16 return true; 17 else 18 return false; 19 } 20 21 //自定义数据无法适应模板,会报如下错误 22 //error C2678: 二进制“==”: 没有找到接受“Person”类型的左操作数的运算符(或没有可接受的转换) 23 //另外,将数据类型具体化,仅仅是数据类型,不能修改返回值类型和函数名 24 template<> bool myComp<Person>(Person &a, Person &b){ 25 if (a.m_Age == b.m_Age){ 26 return true; 27 } 28 else 29 return false; 30 } 31 32 void test02(){ 33 int a = 10, b = 20; 34 int ret = myComp(a, b); 35 cout << "ret = " << ret << endl; 36 37 38 Person p1("tom", 1); 39 Person p2("jerry", 1); 40 int ret1 = myComp(p1, p2); 41 42 cout << "ret = " << ret1 << endl; 43 }
类模板
1. 基本使用
与函数模板相同,紧跟在template声明后,同样一个template对应一个类模板。
与函数模板的区别:(1)类模板不支持自动类型推导;(2)数据类型可以有默认参数.
1 //类模板可以有默认参数 2 //template<class NameType, class AgeType = int> 3 template<class NameType, class AgeType> 4 class Person{ 5 public: 6 Person(NameType name, AgeType age){ 7 m_Name = name; 8 m_Age = age; 9 } 10 11 NameType m_Name; 12 AgeType m_Age; 13 }; 14 15 void test02(){ 16 //类模板不支持自动类型推导 17 //缺少 类模板 "Person" 的参数列表 18 //Person p("啊呀呀", 10); 19 20 Person<string, int> p("啊呀呀", 10); 21 }
2. 成员函数创建时机
成员函数只有在编译或运行时才会创建,仅仅写出来不会报错,如示例所示
1 class Person1{ 2 public: 3 void showPerson1(){ 4 cout << "Person1调用" << endl; 5 } 6 }; 7 8 class Person2{ 9 public: 10 void showPerson2(){ 11 cout << "Person2调用" << endl; 12 } 13 }; 14 15 template<class T> 16 class myclass{ 17 public: 18 T obj; 19 20 void func1(){ 21 obj.showPerson1(); 22 } 23 24 void func2(){ 25 obj.showPerson2(); 26 } 27 }; 28 29 void test02(){ 30 myclass<Person1>tmp; 31 32 tmp.func1(); 33 34 //成员函数只有在程序编译或运行时才会创建,只是写出来不会报错,在编译或运行时会报错 35 //tmp.func2(); 36 }
3. 类模板作函数的参数
有三种方式:(1)显示指定类型;(2)参数模板化;(3)整体模板化
1 template<class NameType, class AgeType> 2 class Person{ 3 public: 4 Person(NameType name, AgeType age){ 5 m_Name = name; 6 m_Age = age; 7 } 8 9 void showPerson(){ 10 cout << "name: " << this->m_Name << " age: " << this->m_Age << endl; 11 } 12 13 NameType m_Name; 14 AgeType m_Age; 15 }; 16 17 //1.显式指定类型 18 void doWork(Person<string, int> &p){ 19 p.showPerson(); 20 } 21 22 //2.参数模板化 23 template<class NameType, class AgeType> 24 void doWork2(Person<NameType, AgeType> &p){ 25 p.showPerson(); 26 } 27 28 //3.整体模板化 29 template<class PersonType> 30 void doWork3(PersonType &p){ 31 p.showPerson(); 32 } 33 34 void test02(){ 35 Person<string, int>p("tom", 10);; 36 doWork(p); 37 doWork2(p); 38 doWork3(p); 39 }
4. 继承中的类模板
如果基类是模板类,则子类需要告诉编译器基类中的T是什么类型,如果不告诉编译器无法通过,不能分配内存。
1 template<class T> 2 class Person{ 3 public: 4 T m_Age; 5 }; 6 //如果子类不显式标明父类中模板的数据类型,会报错:缺少类模板的参数列表 7 class Son :public Person<int>{ 8 9 }; 10 11 //子类中标明的数据类型也可以是模板 12 template<class T1, class T2> 13 class Son1 :public Person<T2>{ 14 T1 m_name; 15 }; 16 17 void test02(){ 18 Son1<string, int>son; //标明Person类中的T为int型,T1为string 19 }
5. 类模板下的类外成员函数实现
普通的类外成员函数实现时,需要将作用域写上即可,但在类模板下,需要将作用域写为类模板形式。另外在类外实现时,需要在类内声明。
1 template<class T1, class T2> 2 class Person{ 3 public: 4 //类内实现 5 Person(T1 name, T2 age; 6 //{ 7 // this->m_Name = name; 8 // this->m_Age = age; 9 //} 10 11 void showPerson(); 12 //{ 13 // cout << "name: " << this->m_Name << "age: " << this->m_Age << endl; 14 //} 15 16 T1 m_Name; 17 T2 m_Age; 18 }; 19 20 //类外实现 21 template<class T1, class T2> 22 Person<T1, T2>::Person(T1 name, T2 age){ 23 this->m_Name = name; 24 this->m_Age = age; 25 } 26 27 template<class T1, class T2> 28 void Person<T1, T2>::showPerson(){ 29 cout << "name: " << this->m_Name << "age: " << this->m_Age << endl; 30 } 31 32 void test02(){ 33 Person<string, int>p("tom", 10); 34 p.showPerson(); 35 }
6. 类模板分文件编写问题
一般进行分文件编写时,.h,.cpp分别写声明和实现,但是由于类模板的成员函数运行阶段才去创建,导致包含.h头文件,不会创建函数的实现,无法解析外部命令,此时
建议类模板不要分文件编写,写到一个类中即可,类内进行声明和实现,最后文件后缀改为hpp.
类模板下的友元函数
1. 类内友元函数实现
类内友元函数实现, friend void printPerson(Person<T1, T2> &p)
1 template<class T1, class T2> 2 class Person{ 3 //友元函数遇到类模板,在类内实现 4 friend void printPerson(Person<T1, T2> &p){ 5 cout << "name : " << p.m_Name << " age :" <<p.m_Age<< endl; 6 } 7 8 public: 9 Person(T1 name, T2 age){ 10 this->m_Name = name; 11 this->m_Age = age; 12 } 13 14 T1 m_Name; 15 T2 m_Age; 16 }; 17 18 void test02(){ 19 Person<string, int> p("tom", 10); 20 printPerson(p); 21 }
2. 类外友元函数实现
类外实现时,需要在类内通过空参数列表,告诉编译器,这是模板函数的声明,不加<>表示普通函数声明。另外,需要让编译器提前看到函数及类的声明。
1 template<class T1, class T2> 2 class Person; 3 4 //让编译器提前看到printPerson声明,但里面有Person声明,需要提前声明Person类 5 template<class T1, class T2> 6 void printPerson(Person<T1, T2> &p); 7 8 template<class T1, class T2> 9 class Person{ 10 //友元函数类外实现时,需要在类内通过空参数列表,告诉编译器,这是模板函数的声明 11 friend void printPerson<>(Person<T1, T2> &p); 12 //{ 13 //cout << "name : " << p.m_Name << " age :" <<p.m_Age<< endl; 14 //} 15 16 public: 17 Person(T1 name, T2 age){ 18 this->m_Name = name; 19 this->m_Age = age; 20 } 21 22 T1 m_Name; 23 T2 m_Age; 24 }; 25 26 template<class T1, class T2> 27 void printPerson(Person<T1, T2> &p){ 28 cout << "name : " << p.m_Name << " age :" << p.m_Age << endl; 29 } 30 31 void test02(){ 32 Person<string, int> p("tom", 10); 33 printPerson(p); 34 }