模板就是建立通用的模具,大大提高复用性 。是c++泛型编程思想和STL技术的主要实现。
函数模板是一种通用函数,用到关键字template和typename(或class),我们可以用类型参数代替具体的数据类型,从而使函数可以处理不同的数据,大大提高了代码复用性。例如:
- template <typename T>
- void myCompare(T &a, T &b) {
- if (a == b) {
- cout << "a == b" << endl;
- }
- else if (a > b) {
- cout << "a > b" << endl;
- }
- else {
- cout << "a < b" << endl;
- }
- }
- void test() {
- int a = 5, b = 10;
- myCompare(a, b);
- double c = 0.9, d = 0.5;
- myCompare(c, d);
- }
代码中模板的定义从template开始,
函数模板用法有两种:1、自动类型推导;2、显示指定类型
- template<typename T>
- T myAdd(T a, T b) {
- return a + b;
- }
- void test() {
- int a = 5, b = 10;
- double c = 3.14, d = 6.28;
- cout << myAdd(a, b) << endl; //自动类型推导
- cout << myAdd<double>(c, d) << endl; //显示指定类型
- }
函数模板的参数列表可以指定默认参数类型,和函数默认参数类型一样,需要从后向前指定。
函数模板的使用中,传入的参数,需要与函数定义中参数列表一致。例如:
- template <typename T,typename K=int>
- void myFunc(T &a, T &b,K &c) {
- cout << "重载模板调用" << endl;
- }
- void test() {
- int a = 5;
- int b = 10;
- char c;
- myFunc(a, b, c); //正确
- //myFunc(a, c, b); //错误
- }
实现一个排序函数模板,能够满足整形、浮点型、字符型等多种内置类型数据数组的排序
- template<typename T>
- void mySort(T arr[], int len) {
- for (int i = 0; i < len; i++) {
- int maxID = i;
- for (int j = i + 1; j < len; j++) {
- if (arr[maxID] < arr[j]) {
- maxID = j;
- }
- }
- if (maxID != i) {
- T temp = arr[maxID];
- arr[maxID] = arr[i];
- arr[i] = temp;
- }
- }
- }
- template<typename T>
- void printArr(T* arr, int len) {
- for (int i = 0; i < len; i++) {
- cout << setw(2) << setiosflags(ios::left) << arr[i];
- }
- cout << endl;
- }
- void test01() {
- int intArr[] = { 1,3,5,6,4,7,2,9,8 };
- int len = sizeof(intArr) / sizeof(int);
- mySort(intArr, len);
- printArr(intArr, len);
- cout << "…………………………………………" << endl;
- char charArr[] = "asdfghjklqwertyuiopzxcvbnm";
- len = sizeof(charArr) / sizeof(char);
- mySort(charArr, len);
- printArr(charArr, len);
-
- }
普通函数支持隐式类型转换;函数模板采用自动类型推导时,不支持参数的隐式类型转换;函数模板使用显示指定类型的方式,可以发生隐式类型转换
- template <typename T>
- T myAdd(T a, T b) {
- return a + b;
- }
- void test() {
- int a = 5, b = 10;
- char c = 'a';
- cout << myAdd(a, b) << endl;
- //cout << myAdd(a, c) << endl;//错误,采用自动类型推导,不能发生隐式类型转换
- cout << myAdd<int>(a, c) << endl;//正确,采用显示指定类型,能够发生隐式类型转换
- }
(1)当普通函数和函数模板同时存在时,优先调用普通函数;
(2)可以通过空模板参数列表来强制调用函数模板;
(3)函数模板可发生重载;
(4)如果函数模板可以产生更好的匹配,优先调用函数模板。
- template <typename T>
- T myAdd(T a, T b) {
- cout << "函数模板调用" << endl;
- return a + b;
- }
-
- int myAdd(int a, int b) {
- cout << "普通函数调用" << endl;
- return a + b;
- }
- template <typename T>
- T myAdd(T a, T b) {
- cout << "函数模板调用" << endl;
- return a + b;
- }
-
- template <typename T>
- T myAdd(T a, T b, T c) {
- cout << "函数模板的重载调用" << endl;
- return a + b + c;
- }
- void test() {
- int a = 5, b = 10;
- cout << myAdd(a, b) << endl; //优先调用普通函数
- cout << myAdd<>(a, b) << endl; //通过空模板参数列表强制调用函数模板
- cout << myAdd(a, b, 10) << endl;//函数模板的重载
- char c = 'c', d = 'd';
- cout << (int)myAdd(c, d) << endl; //函数模板比普通函数更加适配,调用函数模板
- }
输出结果:普通函数调用
函数模板调用
函数模板的重载调用
函数模板调用
例1:当普通函数和函数模板同时存在,且函数原型完全相同,优先调用普通函数。
例2:通过空模板参数列表,强制调用函数模板。
例3:函数模板发生了重载,通过对传入参数的数量控制调用模板
例4:例子中普通函数调用会发生隐式类型转换,函数模板更加适配,则调用函数模板。
TIP:为了避免普通函数和普板函数出现二义性,一般提供了函数模板就不再提供普通函数
函数模板并不是万能的,当参数为数组类型,或者自定义类型(类)时,无法进行部分运算。例如:
- template <typename T>
- void myfunc(T &a, T &b) {
- a = b;
- }
c++提供了函数模板的重载,为特殊类型参数提供具体化模板。语法如下:
- template <typename T>
- void myfunc(T &a, T &b) {
- a = b;
- }
- class Person {
- public:
- string m_name;
- int m_age;
- };
- template<> void myfunc(Person &a, Person &b) {
- a.m_name = b.m_name;
- a.m_age = b.m_age;
- }
特殊类型参数的函数模板具体化后,传入该类型参数会直接调用具体化类型参数的函数模板,不再调用通用函数模板。
类模板和函数模板相似,在template
- template <class NameType,class AgeType>
- class Person {
- public:
- Person(){}
- Person(NameType name, AgeType age) {
- m_name = name;
- m_age = age;
- }
- NameType m_name;
- AgeType m_age;
- };
1、类模板没有自动类型推导的使用方式,只有显式指定类型实例化对象。
2、类模板和函数模板的参数列表中均可以指定默认类型。
- class Person {
- public:
- Person(){}
- Person(NameType name=string, AgeType age=int) {
- m_name = name;
- m_age = age;
- }
- NameType m_name;
- AgeType m_age;
- };
- //Person p("孙悟空", 999); //错误,缺少类模板Person的参数列表
- Person
int>p("猪八戒", 888);//正确,类模板只能使用显示指定类型实例化对象
1、普通类中成员函数在声明时即可创建
2、类模板中成员函数在调用时创建,当模板中T类型不确定时,func1()函数和func2()函数无法创建,只有当实例化Myclass p,确定了T的类型为Person1后调用p.func1()时才会创建。
- class Person1 {
- public:
- void show1() {
- cout << "Person1 show" << endl;
- }
- };
- class Person2 {
- public:
- void show2() {
- cout << "Person2 show" << endl;
- }
- };
- template<class T>
- class MyClass {
- public:
- T obj;
- void func1() {
- obj.show1();
- }
- void func2() {
- obj.show2();
- }
- };
- void test() {
- MyClass
p; - p.func1();
- //p.func2();
- }
-
- int main(int argc, char const *argv[]) {
- test();
- return 0;
- }
1、指定参数类型传参(常用)
2、利用函数模板,声明类模板中参数
3、利用函数模板,声明函数参数后自动推导
- template<class T,class K>
- class Person {
- public:
- Person(T name, K age) {
- this->m_name = name;
- this->m_age = age;
- }
- T m_name;
- K m_age;
- void show() {
- cout << "姓名:" << this->m_name << endl;
- cout << "年龄:" << this->m_age << endl;
- }
- };
- //第一种传参方式:指定参数类型传参
- void printPerson1(Person
int > &p) { - p.show();
- cout << typeid(p).name() << endl;
- }
- //第二种传参方式:利用函数模板,声明类中参数类型
- template<class T,class K>
- void printPerson2(Person
&p) { - p.show();
- cout << typeid(p).name() << endl;
- }
- //第三种传参方式:利用函数模板,声明函数参数类型后自动推导
- template<class T>
- void printPerson3(T &p) {
- p.show();
- cout << typeid(p).name() << endl;
- }
- void test() {
- Person
int > p("唐僧", 30); - printPerson1(p);
- printPerson2(p);
- printPerson3(p);
- }
当子类继承的父类为模板时,声明子类时需要指定父类中的参数类型;
或者子类也作为模板去继承父类模板,用子类模板中的参数类型来指定父类模板参数类型。
- template<class T>
- class Base {
- public:
- T m;
- };
- //子类继承类模板,需指定父类参数类型
- class Son1 :public Base<char> {
-
-
- };
- //子类继承类模板也可声明为类模板
- //此时相当于指定了父类模板中参数类型为T
- template<class T,class K>
- class Son2 :public Base
{ - public:
- K obj;
- };
-
- void test01() {
- Base<char> b; //类模板实例化对象时,需指定参数类型
- Son1 s1; //子类声明时已指定父类参数类型为字符型
- Son2<int,char> s2; //子类为模板时,先指定了父类参数类型为T,也就是实例化时指定的int
- }
1、类模板中成员函数的类外实现,每次要重申模板和模板的参数列表
- template<class TypeName,class TypeAge>
- class Person {
- public:
- TypeName m_name;
- TypeAge m_age;
-
- Person(TypeName name,TypeAge age);
- void show();
- };
-
- template<class TypeName,class TypeAge>
- Person
::Person(TypeName name, TypeAge age) { - this->m_name = name;
- this->m_age = age;
- }
- template<class TypeName,class TypeAge>
- void Person
::show() { - cout << this->m_name << endl;
- cout << this->m_age << endl;
- }
2、分文件编写:因类模板函数是在调用阶段创建,并不是在编译阶段创建,则编译器进行到调用语句时发生错误,无法识别类模板函数。解决办法:在编译阶段将函数体提供给编译器,代替函数原型。可以在编译阶段将函数体所在的cpp文件包含进去,也可以将函数体写进头文件,命名为.hpp文件(约定俗成,不强制),一般用第二种处理方法。
Person.hpp
- #pragma once
- template<class TypeName,class TypeAge>
- class Person
- {
- public:
- TypeName m_name;
- TypeAge m_age;
-
- Person(TypeName name, TypeAge age);
- void show();
- };
- template<class TypeName, class TypeAge>
- Person
::Person(TypeName name, TypeAge age) { - this->m_name = name;
- this->m_age = age;
- }
- template<class TypeName, class TypeAge>
- void Person
::show() { - cout << this->m_name << endl;
- cout << this->m_age << endl;
- }
main.cpp
- #include
- using namespace std;
- #include
- #include"Person.hpp"
- void test() {
- Person
int > p("张三", 20); - p.show();
- }
- int main(int argc, char const *argv[]) {
- test();
- return 0;
- }
类模板的全局友元函数可以分为内部实现和外部实现两种方式
1、内部实现:直接在类内部实现函数体,函数前加上friend友元关键字即可。
2、外部实现:因传入参数不确定类型,则外部全局函数也需要用模板对应类模板
(1)声明类模板
(2)声明全局函数模板(模板参数列表是类模板参数列表)
(3)定义类模板
(4)类内部将全局函数模板声明为友元函数模板
(5)实现函数模板
- #pragma once
- #include
- using namespace std;
- #include
-
- //声明类模板,目的是声明全局函数模板
- template<class TypeName,class TypeAge>
- class Person;
- //声明全局函数模板
- template<class TypeName,class TypeAge>
- void printPerson(Person
&p) ; - //定义类模板
- template<class TypeName,class TypeAge>
- class Person {
- private:
- TypeName m_Name;
- TypeAge m_Age;
- public:
- Person(TypeName name,TypeAge age);
- //外部实现全局友元函数模板
- friend void printPerson<>(Person
&p); - //内部实现全局友元函数
- friend void printPerson1(Person
&p) { - cout << p.m_Name << endl;
- cout << p.m_Age << endl;
- }
- };
- //构造函数的外部实现
- template<class TypeName,class TypeAge>
- Person
::Person(TypeName name, TypeAge age) { - this->m_Name = name;
- this->m_Age = age;
- }
- //全局友元函数模板的外部实现
- template<class TypeName,class TypeAge>
- void printPerson(Person
&p) { - cout << p.m_Name << endl;
- cout << p.m_Age << endl;
- }
用模板实现数组,能够实现对内置数据类型和自定义数据类型的储存和基本操作。通过独立写代码的过程对类的三种权限复习一下;对运算符的重载复习一下;对new的用法理解加深了一些。
MyArray.hpp
- #pragma once
- #include
- using namespace std;
- //定义数组类模板
- template<class T>
- class MyArray {
- private:
- int m_size = 0; //元素数量
- int m_capacity ; //数组容量
- T *m_array = NULL; //数组指针
- public:
- MyArray(int capacity);
- MyArray(const MyArray& array);
- void add(const T &value); //添加元素
- void del(const T &value); //删除元素
- int search(const T &value); //搜索元素
- void print(); //打印数组
- T& operator[](int index); //[]重载
- MyArray
& operator=(const MyArray& array);//=重载 - bool isfull(); //判定数组是否已满
- ~MyArray();
- };
- //普通构造函数
- template<class T>
- MyArray
::MyArray(int capacity) { - this->m_size = 0;
- this->m_capacity = capacity;
- this->m_array = new T[capacity];
- }
- //拷贝构造函数,利用=重载避免浅拷贝
- template<class T>
- MyArray
::MyArray(const MyArray& array) { - *this = array;
- }
- //=号重载,返回对象本身实现连续复制
- template<class T>
- MyArray
& MyArray::operator=(const MyArray& array) { - this->m_capacity = array.m_capacity;
- this->m_size = array.m_size;
- if (this->m_array) {
- delete this->m_array;
- }
- this->m_array = new T[m_capacity];
-
- for (int i = 0; i < this->m_size; i++) {
- this->m_array[i] = array.m_array[i];
- }
- return *this;
- }
- //[]重载,取当前index下标元素
- template<class T>
- T& MyArray
::operator[](int index) { - return m_array[index];
- }
- //判定数组是否已满
- template<class T>
- bool MyArray
::isfull() { - return this->m_size == this->m_capacity;
- }
- //添加元素
- template<class T>
- void MyArray
::add(const T &value) { - if (!this->isfull()) {
- this->m_array[this->m_size++] = value;
- }
- }
- //删除元素
- template<class T>
- void MyArray
::del(const T &value) { - int index = search(value);
- while (index != -1) {
- for (int i = index; i < m_size - 1; i++) {
- m_array[i] = m_array[i + 1];
- }
- m_size--;
- index = search(value);
- }
- }
- //查找元素
- template<class T>
- int MyArray
::search(const T &value) { - int index = -1;
- for (int i = 0; i < m_size; i++) {
- if (this->m_array[i] == value) {
- index = i;
- break;
- }
- }
- return index;
- }
- //打印元素
- template<class T>
- void MyArray
::print() { - for (int i = 0; i < m_size; i++) {
- cout << m_array[i] ;
- if (i != m_size - 1) {
- cout << " ";
- }else{
- cout << endl;
- }
- }
- cout << endl;
- }
- template<class T>
- MyArray
::~MyArray() { - if (m_array) {
- delete m_array;
- m_array = NULL;
- }
- }
main.cpp
- #include
- using namespace std;
- #include
- #include"myArray.hpp"
-
- void test() {
- MyArray<int> *pA_int = new MyArray<int>(10);
- pA_int->add(3);
- pA_int->add(5);
- pA_int->add(7);
- pA_int->add(8);
- pA_int->add(8);
- cout << (*pA_int)[1] << endl;
- pA_int->print();
-
- MyArray<int> pA_int2(*pA_int);
- pA_int2.print();
- pA_int2.del(8);
- pA_int2.print();
- }
-
- int main(int argc, char const *argv[]) {
- test();
- return 0;
- }