模板就是简历通用的模具,大大提高 复用性。
1.1 概念
特点:
模板不可以直接使用,只是一个框架
模板的通用并不是万能的
1.2 函数模板
泛型编程,主要利用的技术 是模板
两种模板:函数模板和类模板
1.2.1 函数模板用法
建立一个通用函数,返回类型和形参类型可以不具体制定,用一个虚拟的类型来代表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 template <typename T>#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 , b = 20 ; double c = 1.1 , d = 2.2 ; mySwap (a, b); cout << a << " " << b << endl; mySwap <double >(c, d); cout << c << " " << d << endl; }
利用关键字template
两种方式:自动类型推导,显式指定类型
模板的目的是为了提高复用性,将类型参数化
1.2.2 函数模板注意事项
注意事项:
·自动类型推导,必须推导出一致的数据类类型T,才可以使用
·模板必须要确定出的数据类型,才可以使用
1.2.3 排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <iostream> using namespace std;template <typename T>void mySwap (T& a, T& b) { T temp = a; a = b; b = temp; }template <typename T>void mySort (T arr[], int len) { for (int i = 0 ; i < len; i++) { int max = i; for (int j = i; j < len; j++) { if (arr[max] < arr[j]) { max = j; } } if (max!=i) { mySwap (arr[max], arr[i]); } } }template <class T>void printArray (T arr[], int len) { for (int i = 0 ; i < len; i++) { cout << arr[i] << " " ; } cout << endl; }void test () { char charArr[] = "badcfe" ; int length = sizeof (charArr) / sizeof (charArr[0 ]); mySort (charArr, length); printArray (charArr, length); }int main () { test (); }
1.2.4 普通函数与 函数模板的区别
普通函数与函数模板区别:
·普通函数调用时可以发生自动类型转换(隐式类型转换)
·函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
如果利用显示指定类型的方式,可以发生隐式类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> using namespace std;template <class T>T myAdd (T a,T b) { return a + b; }void test () { cout << myAdd (1 , 2 ) << endl; cout << myAdd <int >(1 , 'a' ) << endl; cout << myAdd <char >(1 , 'a' ) << endl; }int main () { test (); }
1.2.5 调用规则如下:
1.如果函数模板和普通函数都可以实现,优先调用普通函数
2.可以通过空模板参数列表来强制调用函数模板
3.函数模板也可以发生重载
4.如果函数模板可以产生更好的匹配,优先调用函数模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> using namespace std;void myPrint (int a, int b) { cout << "hello" << endl; }template <class T>void myPrint (T a, T b) { cout << "temp" << endl; }template <class T>void myPrint (T a, T b,T c) { cout << "temp ccc" << endl; }void test () { myPrint (10 , 10 ); myPrint<>(10 , 10 ); myPrint<>('a' , 'b' ); myPrint<>(10 , 10 , 10 ); }int main () { test (); }
既然提供了 函数模板,最好 就不要提供普通函数,否则 会出现二义性
1.2.6 模板的 局限性
在上述代码中,如果T的数据类型传入的是像Perso这样的自定义数据类型,也无法正常运行
因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <iostream> using namespace std;class Person {public : Person (string m_name, int m_age) :name (m_name), age (m_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& a, Person& b) { if (a.age ==b.age) { return true ; } else { return false ; } }void test () { int a = 10 ; int b = 20 ; cout << myCompare (a, b) << endl; Person p1 ("Tom" , 10 ) ; Person p2 ("Tim" , 10 ) ; cout << myCompare (p1, p2) << endl; }int main () { test (); }
1.3 类模板
1.3.1 类模板语法
类模板作用:
·建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。
1 2 template <typename T>class
解释:
template-声明创建模板
typename-表面其后面的符号是一种数据类型,可以用class代替
T通用的数据类型,
名称可以替换,通常为大写字母
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> using namespace std;template <class NameType , class AgeType >class Person {public : Person (NameType n, AgeType a) :name (n), age (a) {} void ShowPerson () { cout << name << endl; cout << age << endl; } NameType name; AgeType age; };int main () { Person<string, int > p1 ("Tom" , 99 ) ; p1. ShowPerson (); }
1.3.2
类模板与函数模板区别主要有两点:
1.类模板没有自动类型推导的使用方式
2.类模板在模板参数列表中可以有默认参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> using namespace std;template <class NameType , class AgeType = int >class Person {public : Person (NameType n, AgeType a) :name (n), age (a) {} void ShowPerson () { cout << name << endl; cout << age << endl; } NameType name; AgeType age; };int main () { Person<string> p1 ("Tom" , 99 ) ; p1. ShowPerson (); }
1.3.3 类模板中成员函数创建时间
类模板中成员函数和普通类中成员函数创建时机是有区别的:
普通类中的成员函数一开始就可以创建
·类模板中的成员函数在调用时才创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> using namespace std;class Person1 {public : void ShowPerson1 () { cout << "name" << endl; } };class Person2 {public : void ShowPerson2 () { cout << "age" << endl; } };template <class T >class MyClass {public : T obj; void func1 () { obj.ShowPerson1 (); } void func2 () { obj.ShowPerson2 (); } };int main () { MyClass<Person1>m; m.func1 (); }
1.3.4 类模板对象做函数参数
学习目标:
·类模板实例化出的对象,向函数传参的方式
一共有三种传入方式:
1.指定传入的类型一直接显示对象的数据类型(最常用)
2.参数模板化
一将对象中的参数变为模板进行传递
3.整个类模板化
一将这个对象类型模板化进行传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <iostream> using namespace std;template <class T1 , class T2 >class Person {public : Person (T1 m_name, T2 m_age) :name (m_name), age (m_age) {} void ShowPerson () { cout << name << endl; cout << age << endl; } T1 name; T2 age; };void printPerson1 (Person<string, int >&p) { p.ShowPerson (); }template <class T1, class T2>void printPerson2 (Person<T1, T2>& p) { p.ShowPerson (); }template <class T>void printPerson3 (T& p) { p.ShowPerson (); cout << typeid (T).name () << endl; }int main () { Person<string, int >p ("sun" , 100 ); printPerson1 (p); Person<string, int >p2 ("zhu" , 100 ); printPerson2 (p2); Person<string, int >p3 ("tang" , 100 ); printPerson3 (p3); }
1.3.5 类模板与继承
当类模板碰到继承时,需要注意一下几点:
·当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
·如果不指定,编泽器无法给子类分配内存
·如果想灵活指定出父类中T的类型,子类也需变为类模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> using namespace std;template <class T >class Base { T m; };class Son :public Base<int > { };template <class T1 , class T2 >class Son2 :public Base<T2> { T1 obj; };int main () { Son s1; Son2 <int , char > s2; }
1.3.6 类模板函数类外实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> using namespace std;template <class T1 , class T2 >class Person {public : Person (T1 m_name, T2 m_age); void ShowPerson () ; T1 name; T2 age; };template <class T1 , class T2 > Person<T1,T2>::Person (T1 m_name, T2 m_age) { this ->name = (m_name); this ->age = (m_age); }template <class T1 , class T2 >void Person<T1, T2>::ShowPerson () { cout << this ->name << endl; cout << this ->age << endl; }int main () { Person<string, int >p ("sun" , 100 ); p.ShowPerson (); }
1.3.7 类模板份文件编写
问题:
。
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
。解决方式1:直接包含.cpp源文件
。解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <iostream> #include "Person.hpp" using namespace std;int main () { Person<string, int >p ("sun" , 100 ); p.ShowPerson (); }#pragma once #include <iostream> using namespace std;template <class T1 , class T2 >class Person {public : Person (T1 m_name, T2 m_age); void ShowPerson () ; T1 name; T2 age; };template <class T1 , class T2 > Person<T1, T2>::Person (T1 m_name, T2 m_age) { this ->name = (m_name); this ->age = (m_age); }template <class T1 , class T2 >void Person<T1, T2>::ShowPerson () { cout << this ->name << endl; cout << this ->age << endl; }
1.3.类模板与友元
全局函数类内实现·直接在类内声明友元 即可
全局函数类外实现·需要提前让编译器知道全局函数的存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #pragma once #include <iostream> using namespace std;template <class T1 , class T2 >class Person ;template <class T1 , class T2 >void ShowPerson2 (Person<T1, T2> p) { cout << p.name << endl; cout << p.age << endl; }template <class T1 , class T2 >class Person { friend void ShowPerson (Person<T1, T2> p) { cout << p.name << endl; cout << p.age << endl; } friend void ShowPerson2 <>(Person<T1, T2> p);public : Person (T1 m_name, T2 m_age); private : T1 name; T2 age; };template <class T1 , class T2 > Person<T1, T2>::Person (T1 m_name, T2 m_age) { this ->name = (m_name); this ->age = (m_age); }int main () { Person<string, int >p ("sun" , 100 ); ShowPerson2 (p); ShowPerson (p); }