模板分为函数模板和类模板
函数模板
模板的语法
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定
1 template<typename T> 2 void mySwap(T& a, T& b) { 3 T temp = a; 4 a = b; 5 b = temp; 6 } 7 int main() { 8 int a = 10; 9 int b = 20; 10 //自动类型推导 11 /*mySwap(a, b);*/ 12 //显示指定类型 13 mySwap<int>(a, b); 14 cout << "a=" << a << endl; 15 cout << "b=" << b << endl; 16 system("pause"); 17 return 0; 18 }
函数模板的注意事项
- 自动类型推导,必须推导出一致的数据类型T,才能使用
- 模板必须要确定出T的数据类型才能够使用
1 template<typename T> 2 void fun() { 3 4 } 5 int main() { 6 fun<int>(); 7 system("pause"); 8 return 0; 9 }
一个template只能对应一个函数模板
普通函数与模板函数的区别
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
1 template<typename T1> 2 void mySwap1(T1 a, T1 b) { 3 T1 temp = a; 4 a = b; 5 b = temp; 6 } 7 void mySwap2(int a, int b) { 8 int temp = a; 9 a = b; 10 b = temp; 11 } 12 void fun() { 13 14 } 15 int main() { 16 int a = 10; 17 int b = 20; 18 char c = 'a'; 19 20 /*mySwap1(a, c);*///报错,不能发生隐式类型转换,况且这种写法编译器根本就不知道T是什么类型 21 //首先是编译器根据int推断出T是int类型,然后将形参位置的T换成int类型 22 //然后这种写法可以发生隐式转换,char类型转换成了int类型 23 mySwap1<int>(a, c); 24 mySwap2(a, c); 25 system("pause"); 26 return 0; 27 }
函数模板和普通函数的调用规则
1.如果函数模板和普通函数都可以调用,则优先调用普通函数
1 void myPrint(int a, int b) { 2 cout << "调用的普通函数" << endl; 3 } 4 template<typename T> 5 void myPrint(T a, T b) { 6 cout << "调用的是函数模板" << endl; 7 } 8 void test01() { 9 int a = 10; 10 int b = 20; 11 myPrint(a, b); 12 } 13 int main() { 14 test01(); 15 system("pause"); 16 return 0; 17 }
如果此时我们的普通函数只是做了声明,但没有定义,编译器会报错
1 void myPrint(int a, int b); 2 template<typename T> 3 void myPrint(T a, T b) { 4 cout << "调用的是函数模板" << endl; 5 } 6 void test01() { 7 int a = 10; 8 int b = 20; 9 myPrint(a, b); 10 } 11 int main() { 12 test01(); 13 system("pause"); 14 return 0; 15 }
此时如果调用的是空模板(见第二点),编译器就不会报错了
2.可以通过空模板参数列表来强制调用函数模板
void myPrint(int a, int b) { cout << "调用的普通函数" << endl; } template<typename T> void myPrint(T a, T b) { cout << "调用的是函数模板" << endl; } void test01() { int a = 10; int b = 20; myPrint<>(a, b); } int main() { test01(); system("pause"); return 0; }
3.函数模板可以发生函数重载
4.如果函数模板可以产生更好的匹配,优先调用函数模板
void myPrint(int a, int b) { cout << "调用的普通函数" << endl; } template<typename T> void myPrint(T a, T b) { cout << "调用的是函数模板" << endl; } void test01() { char a = 'a'; char b = 'b'; myPrint(a, b); } int main() { test01(); system("pause"); return 0; }
模板的局限性
两个自定义类型不适用函数模板中的功能
1 class Person { 2 public: 3 Person(string name, int age) :name_(name), age_(age) { 4 5 } 6 private: 7 string name_; 8 int age_; 9 }; 10 template<typename T> 11 bool myPrint(T a, T b) { 12 if (a == b) { 13 return true; 14 } 15 else { 16 return false; 17 } 18 } 19 20 void test01() { 21 Person p1("TOM", 10); 22 Person p2("TOM", 10); 23 myPrint(p1, p2); 24 } 25 int main() { 26 test01(); 27 system("pause"); 28 return 0; 29 }
解决方法之一就是将比较运算符重载(重载也是相当于一个对象而言的,因此和下面的解决办法是等效的)
另外一种解决方法就是具体化Person函数的实现版本
#include<iostream> #include<string> using namespace std; class Person { public: Person(string name, int age) :name_(name), age_(age) { } string name_; int age_; }; template<class T> bool myCompare(T& a, T& b) { if (a == b) { return true; } else { return false; } } template<> bool myCompare(Person& p1, Person& p2) { if (p1.name_ == p2.name_ && p1.age_ == p2.age_) { return true; } else { return false; } } void test01() { int a = 10; int b = 20; bool ret = myCompare(a, b); if (ret) { cout << "a==b" << endl; } else { cout << "a!=b" << endl; } } void test02() { Person p1("TOM", 10); Person p2("TOM", 10); bool ret = myCompare(p1, p2); if (ret) { cout << "a==b" << endl; } else { cout << "a!=b" << endl; } } int main() { test01(); system("pause"); return 0; }
这个地方一定要注意的是不能单独写具体化Person版本
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 class Person 5 { 6 public: 7 Person(string name, int age) :name_(name), age_(age) { 8 9 } 10 string name_; 11 int age_; 12 }; 13 14 template<> bool myCompare(Person& p1, Person& p2) { 15 if (p1.name_ == p2.name_ && p1.age_ == p2.age_) 16 { 17 return true; 18 } 19 else 20 { 21 return false; 22 } 23 } 24 void test01() 25 { 26 int a = 10; 27 int b = 20; 28 bool ret = myCompare(a, b); 29 if (ret) 30 { 31 cout << "a==b" << endl; 32 } 33 else 34 { 35 cout << "a!=b" << endl; 36 } 37 } 38 void test02() 39 { 40 Person p1("TOM", 10); 41 Person p2("TOM", 10); 42 bool ret = myCompare(p1, p2); 43 if (ret) 44 { 45 cout << "a==b" << endl; 46 } 47 else 48 { 49 cout << "a!=b" << endl; 50 } 51 } 52 int main() { 53 test01(); 54 system("pause"); 55 return 0; 56 }
类模板
类模板的作用:
创建一个通用类,类中的成员数据类型可以不具体指定,用一个虚拟的类型代表
可以使用模板重载有参构造,但注意不能和普通有参构造的参数类型相同,这样就是重复定义了
template<class T> class Person { public: Person(T a) { cout << "Person使用模板定义的有参构造" << endl; } Person(int b) { cout << "Person普通的有参构造" << endl; } }; int main() { Person<string> p("C++");//这个地方不能用int system("Pause"); return 0; }
当类模板中使用其他模板时,允许先不将其他模板的类型赋全,而等到实例化的时候再将类型补全
template<class M,class T> struct MyStruct { M val_; T val2_; }; template<class M,class T> class Person { public: MyStruct<int,T> str; }; int main() { Person<int, string>p;//实例化的时候第一个类型只能是int,不能改成其他类型 p.str.val_ = 0; p.str.val2_ = "C++"; cout<<typeid(p.str.val2_).name() system("Pause"); return 0; }
类模板和函数模板的区别
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数类表中可以有默认参数
默认参数:
类模板中的成员函数创建时机
普通类中的成员函数一开始就可以创建
类模板中的成员函数在调用时才创建
1 class Person1 { 2 public: 3 void showPerson1() { 4 cout << "Person1 show" << endl; 5 } 6 }; 7 class Person2 { 8 public: 9 void showPerson2() { 10 cout << "Person2 show" << endl; 11 } 12 }; 13 template<class T> 14 class Myclass 15 { 16 public: 17 T obj; 18 void func1() 19 { 20 obj.showPerson1(); 21 } 22 void func2() 23 { 24 obj.showPerson2(); 25 } 26 };
fun1(),fun2()只有在调用的时候才会创建,因此其内部可以任意的使用成员属性。
注意以下使用成员属性的方法是不正确的
1 class Person1 { 2 public: 3 void showPerson1() { 4 cout << "Person1 show" << endl; 5 } 6 }; 7 class Person2 { 8 public: 9 void showPerson2() { 10 cout << "Person2 show" << endl; 11 } 12 }; 13 template<class T> 14 class Myclass 15 { 16 public: 17 T obj; 18 obj.showPerson1(); 19 void func1() 20 { 21 obj.showPerson1(); 22 } 23 void func2() 24 { 25 obj.showPerson2(); 26 } 27 };
类模板对象做函数参数
1 #include<iostream> 2 #include<string> 3 #include<vector> 4 #include<iterator> 5 using std::cout; 6 using std::endl; 7 using std::string; 8 template<class T1,class T2> 9 class Person 10 { 11 public: 12 Person(T1 name, T2 age) :name_(name), age_(age) 13 { 14 15 } 16 void showPerson() 17 { 18 cout << "姓名" << name_ << "年龄" << age_ << endl; 19 } 20 T1 name_; 21 T2 age_; 22 }; 23 24 //指定传入类型 25 void printPerson1(Person<string, int>& p) 26 { 27 p.showPerson(); 28 } 29 void test01() 30 { 31 Person<string, int>p("孙悟空", 100); 32 printPerson1(p); 33 } 34 //指定参数模板化 35 template<class T1,class T2> 36 void printPerson2(Person<T1, T2>& p) 37 { 38 p.showPerson(); 39 } 40 void test02() 41 { 42 Person<string, int>p("猪八戒", 90); 43 printPerson2(p); 44 } 45 template<class T> 46 void printPerson3(T& p) 47 { 48 p.showPerson(); 49 } 50 void test03() 51 { 52 Person<string, int>p("唐僧", 20); 53 printPerson3(p); 54 } 55 int main(){ 56 test01(); 57 test02(); 58 test03(); 59 system("pause"); 60 return 0; 61 }
最常用的是第一种方法
类模板与继承
1 template<class T> 2 class Base 3 { 4 T m_; 5 }; 6 //错误的必须要知道父类中T的数据类型,才能继承给子类 7 //class Son :public Base 8 //{ 9 // 10 //}; 11 class Son :public Base<int> 12 { 13 14 }; 15 void test01() 16 { 17 Son s1; 18 }
1 template<class T> 2 class Base 3 { 4 T m_; 5 }; 6 7 //如果想要灵活的指定父类中的T类型,子类也需要变类模板 8 template<class T1,class T2> 9 class Son :public Base<T2> 10 { 11 public: 12 Son() 13 { 14 cout << "T1的类型为:" << typeid(T1).name() << endl; 15 cout << "T2的类型为:" << typeid(T2).name() << endl; 16 } 17 T1 obj; 18 }; 19 void test02() 20 { 21 Son<int, char>s; 22 }
模板成员函数的类外实现
构造函数类外实现
成员函数的类外实现
类模板的分文件编写
(85条消息) C++中类模板分文件编写出现问题的原因(涉及函数定义作用、编译过程等)_类的分文件编写,易错点_对的时间点的博客-CSDN博客
参考上面文章可以知道,如果文件中只有函数的声明(也就是只包含头文件),没有函数的定义那么调用了这个函数,编译器就要让链接器去找这个函数的定义,而唯一写了这个定义的cpp文件里面并没有函数的调用,那么这个成员函数就像没写一样,那么链接器就找不到函数的定义,也就是无法解析。如果包含了cpp文件那么main函数中既有函数的声明又有函数的定义(相当于没有分文件编写),那么就可以直接调用了,就不用让链接器去找函数的定义了,那么就不存在这个问题了。
以上论述是将头文件和cpp文件分来编写,这种方法不太常用。
比较常用的方法是将类模板的定义和成员函数类外定义的代码写到同一个文件夹下,并取名为".hpp",然后让用到这个类的文件包含这个".hpp"文件就可以了。
类模板作为友元
template<class T1,class T2> class Person { //注意此处的T1,T2相当于这个类模板的,所以这个函数是一个普通函数 //因此要搞清楚template<class T1,class T2>这个语句是作用于谁的 //这个地方应该是作用域类的,因此类是模板,函数不是模板 friend void printPerson(Person<T1, T2>p); public: Person(T1 name, T2 age) :name_(name), age_(age) { } private: T1 name_; T2 age_; }; //这个地方template<class T1, class T2>是作用域函数的,因此函数是模板 template<class T1, class T2> void printPerson(Person<T1, T2>p) { cout << "类外实现--姓名:" << p.name_ << "年龄:" << p.age_ << endl; } void test01() { Person<string, int>p("TOM", 20); printPerson(p); }
因此友元声明声明的不是类外的全局函数模板,会出现以下的错误:
1 template<class T1,class T2> 2 class Person 3 { 4 //这个地方template<class T1, class T2>是作用域函数的,因此函数是模板 5 template<class T1, class T2> 6 friend void printPerson(Person<T1, T2>p); 7 public: 8 Person(T1 name, T2 age) :name_(name), age_(age) 9 { 10 11 } 12 private: 13 T1 name_; 14 T2 age_; 15 }; 16 17 template<class T1, class T2> 18 void printPerson(Person<T1, T2>p) 19 { 20 cout << "类外实现--姓名:" << p.name_ << "年龄:" << p.age_ << endl; 21 } 22 void test01() 23 { 24 Person<string, int>p("TOM", 20); 25 printPerson(p); 26 }