目录
1.0 模板概论
C++提供了函数模板。所谓函数模板,实际上是建立了一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就是函数模板。
凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只要在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。
1.1 函数模板
1.1.1 模板声明及模板使用小案例
1.实现任意两个数交换
void myswap(T& a,T& b) { T temp = a; a = b; b = temp; } void test01() { int a = 1; int b = 2; float c = 1.1; float d = 2.2; myswap(a,b); myswap(c,d); cout << "a = " << a << endl; cout << "b = " << b << endl; cout << "c = " << c << endl; cout << "d = " << d << endl; }
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 a = 2 b = 1 c = 2.2 d = 1.1
2.模板声明
template<typename T>
这里的T是指代通用数据类型,告诉编译器如果下面的函数或者类中出现T不要报错。
3.模板使用
I.自动类型推导
自动判断a,b是int类型,必须推导出是一致的T数据类型,才能正确的使用模板。
II.显示指定类型
void test01() { int a = 1; int b = 2; float c = 1.1; float d = 2.2; myswap<int>(a,b); myswap<float>(c,d); cout << "a = " << a << endl; cout << "b = " << b << endl; cout << "c = " << c << endl; cout << "d = " << d << endl; }
III.模板不能单独使用,必须指定T才可以使用
4.案例:对char和int数组进行排序
void myswap(T& a,T& b) { T temp = a; a = b; b = temp; } template<typename TE> void mySort(TE arr[],int len) { for(int i=0;i<len;i++) { int max = i; for(int j=i+1;j<len;j++) { if(arr[max]<arr[j]) { max = j; } } if(i!=max) { myswap(arr[i],arr[max]); } } } template<typename T> void printarray(T arr[],int len) { for(int i=0;i<len;i++) cout << arr[i] ; cout << endl; } void test02() { char chararray[] = "helloworld"; int charlen = strlen(chararray); mySort(chararray,charlen); printarray(chararray,charlen); int intarray[] = {5,9,7,4,3,6,8}; mySort(intarray,7); printarray(intarray,7); }
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 wroolllhed 9876543
1.1.2 函数模板和普通函数的区别以及调用规则
1. 函数模板和普通函数的区别
void test03() { int a = 10; int b = 20; char c = 'c'; //myadd1(a,c); error 如果使用自动类型推导,是不允许发生隐式类型转换的 cout << myadd2(a,c) << endl; //普通函数允许发生隐式类型转换 }
函数模板如果使用自动类型推导,是不允许发生隐式类型转换的。
普通函数允许发生隐式类型转换
2. 函数模板和普通函数的调用规则
I.如果函数模板和普通函数都可以调用,那么优先调用普通函数。
template<typename T> void printmt(T a,T b) { cout << "template function is running" << endl; } void printmt(int a,int b) { cout << "original function is running" << endl; } void test04() {
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 original function is running
II.如果想强制调用函数模板,可以使用空模板参数列表
template<typename T> void printmt(T a,T b) { cout << "template function is running" << endl; } void printmt(int a,int b) { cout << "original function is running" << endl; } void test04() { printmt<>(10,20); }
III.函数模板也可以发生函数重载
template<typename T> void printmt(T a,T b) { cout << "template function 2 is running" << endl; } template<typename T> void printmt(T a,T b,T c) { cout << "template function 3 is running" << endl; } void printmt(int a,int b) { cout << "original function is running" << endl; } void test04() { printmt<>(10,20,30); }
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 template function 3 is running
IV.如果函数模板能产生更好的转换,优先选择函数模板
template<typename T> void printmt(T a,T b) { cout << "template function 2 is running" << endl; } template<typename T> void printmt(T a,T b,T c) { cout << "template function 3 is running" << endl; } void printmt(int a,int b) { cout << "original function is running" << endl; } void test04() { printmt<>('a','b'); }
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 template function 2 is running
1.1.3 函数机制和模板局限性
1.局限性
I.编译器并不是把函数模板处理成能够处理任何类型的数据---通过具体化解决
代码:看两个变量是否相等
class Person { public: Person(string name,int age) { this->name = name; this->age = age; } string name; int age; }; template<class T> bool mycompare(T& a,T& b) { if(a==b) return true; } template<> bool mycompare(Person& a,Person& b) { if(a.name==b.name && a.age==b.age) return true; else return false; } void test05() { int a = 10; int b = 10; Person p1("Tom",19); Person p2("Tom",19); bool ret = mycompare(a,b); if(ret) { cout << "a = b" << endl; } else { cout << "a !=b "; } }
II.函数模板通过具体类型产生不同的函数
template<class T> bool mycompare(T& a,T& b) { if(a==b) return true; } template<> bool mycompare(Person& a,Person& b) { if(a.name==b.name && a.age==b.age) return true; else return false; }
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 a = b
III.编译器会对函数模板进行再次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
1.1.4 类模板
1.声明
template<class NAMETYPE,class AGETYPE> class Person { public: Person(NAMETYPE name,AGETYPE age) { this->name = name; this->age = age; } void showperson() { cout << "name is : " << this->name << endl; cout << "age is : " << this->age << endl; } NAMETYPE name; AGETYPE age; };
我们可以有多种模板声明,函数模板也可以。
2.调用
I.自动类型推导:类模板不可以用(对比于函数模板)
Person p1("sunwukong",100);
这是错误的,在函数模板可以用在类模板不可以用。
II.指定类型
void test01() { Person <string,int>p1("sunwukong",100); p1.showperson(); }
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 name is : sunwukong age is : 100
3.类模板中成员函数创建时机
class Person1 { public: void showperson1() { cout << "personshow1 is running" << endl; } }; class Person2 { public: void showperson2() { cout << "personshow2 is running" << endl; } }; template<class T> class Myclass { public: void Func1() { obj.showperson1(); } void Func2() { obj.showperson2(); } T obj; }; void test01() { Myclass<Person1> p1; p1.Func1(); //p1.Func2(); Myclass<Person2> p2; //p2.Func1(); p2.Func2(); }
我们可以看到,p1调用不了Func2,p2调用不了Func1。
类模板中成员函数并不是一开始创建的,创建时机是在运行阶段确定出T的类型才创建的。
4.类模板做函数参数
I.指定传入类型
template<class T1,class T2> class Person { public: Person(T1 name,T2 age) { this->name = name; this->age = age; } void showperson() { cout << "name is : " << this->name << endl; cout << "age is : " << this->age << endl; } T1 name; T2 age; }; void dowork(Person<string,int>&p) { p.showperson(); } void test01() { Person<string,int>p("sunwukong",99); dowork(p); }
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 name is : sunwukong age is : 99
不用编译器猜,自己提供数据类型
II.参数模板化
template<class T1,class T2> void dowork(Person<T1,T2>&p) { p.showperson(); } void test02() { Person<string,int>p("zhubajie",98); dowork(p); }
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 name is : zhubajie age is : 98
让编译器猜两个数据类型是什么
III.将整个类模板化
template<class T> void dowork3(T& p) { p.showperson(); } void test03() { Person<string,int>p("tangseng",10000); dowork3(p); }
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 name is : tangseng age is : 10000
让编译器猜是哪个类
5.类模板遇到继承及解决
I.必须要指定出父类中T的类型,才能给子类分配内存
template<class T> class Base { public: T m_a; }; class son : public Base<int> { };
II.更灵活的方式
template<class T> class Base { public: T m_a; }; template<class T1,class T2> class son : public Base<T2> { public: T1 m_b; }; void test04() { son <int ,double> s; } //m_a double m_b int
6.类模板的成员函数类外实现
template<class T1,class T2> class Person { public: Person(T1 name,T2 age); void showperson(); T1 name; T2 age; }; template<class T1,class T2> Person<T1,T2>::Person(T1 name,T2 age) { this->name = name; this->age = age; } template<class T1,class T2> void Person<T1,T2>:: showperson() { cout << "name is : " << this->name << endl; cout << "age is : " << this->age << endl; }
7.类模板的分文件编写问题及解决
I.头文件.hpp
// // Created by lhw on 2022/6/10. // #ifndef DAY8_PERSON_H #define DAY8_PERSON_H #include <iostream> using namespace std; template<class T1,class T2> class Person { public: Person(T1 name,T2 age); void showperson(); T1 name; T2 age; }; template<class T1,class T2> Person<T1,T2>::Person(T1 name,T2 age) { this->name = name; this->age = age; } template<class T1,class T2> void Person<T1,T2>:: showperson() { cout << "name is : " << this->name << endl; cout << "age is : " << this->age << endl; } #endif //DAY8_PERSON_H
II.源文件 main.cpp
#include <cstring> #include "person.hpp" void dowork(Person<string,int>&p) { p.showperson(); } void test01() { Person<string,int>p("sunwukong",99); dowork(p); } int main() { test01(); return 0; }
8.类模板碰到友元函数及其解决
I.友元函数类内实现
template<class T1,class T2> class Person { public: friend void printperson(Person<T1,T2>&p) { cout << "name is : " << p.name << "age is : " << p.age; } public: Person(T1 name,T2 age) { this->name = name; this->age = age; } private: T1 name; T2 age; }; void test03() { Person<string,int>p("tom",100); printperson(p); } int main() { test03(); return 0; }
可以当作全局函数去调用。
II.友元函数类外实现1:有点复杂
template<class T1,class T2> class Person; template<class T1,class T2> void printperson(Person<T1,T2>&p); template<class T1,class T2> class Person { public: friend void printperson<>(Person<T1,T2>&p); public: Person(T1 name,T2 age) { this->name = name; this->age = age; } private: T1 name; T2 age; }; template<class T1,class T2> void printperson(Person<T1,T2>&p) { cout << "name is : " << p.name << "age is : " << p.age; } void test03() { Person<string,int>p("tom",100); printperson(p); }
III.友元函数类外实现2
template<class T1,class T2> class Person; template<class T1,class T2> void printperson2(Person<T1,T2>&p) { cout << "name is : " << p.name << "age is : " << p.age; } template<class T1,class T2> class Person { public: friend void printperson2<>(Person<T1,T2>&p); public: Person(T1 name,T2 age) { this->name = name; this->age = age; } private: T1 name; T2 age; }; void test03() { Person<string,int>p("tom",100); printperson2(p); }
1.1.5 案例:myarray
1.myarray.hpp
// // Created by lhw on 2022/6/10. // #ifndef DAY8_MYARRAY_HPP #define DAY8_MYARRAY_HPP template<class T> class Myarray { public: Myarray() { } Myarray(int capacity) { this -> capacity = capacity; this -> size = 0; this -> pAddress = new T[this->capacity]; } Myarray(const Myarray & myarray) { this -> capacity = myarray.capacity; this -> size = myarray.size; this -> pAddress = new T[this->capacity]; for (int i = 0 ; i<myarray.size ; i++) { this -> pAddress[i] = myarray.pAddress[i]; } } Myarray& operator=(const Myarray & myarray) { if(this->pAddress) { delete []this->pAddress; this -> pAddress = NULL; } this -> capacity = myarray.capacity; this -> size = myarray.size; this -> pAddress = new T[this->capacity]; for (int i = 0 ; i<myarray.size ; i++) { this -> pAddress[i] = myarray.pAddress[i]; } return *this; } ~Myarray() { if(this->pAddress) { delete []this->pAddress; this -> pAddress = NULL; } } T& operator[](int index) { return this->pAddress[index]; } void pushback(const T & value) { if(this -> capacity == this -> size) { return; } this -> pAddress[this->size] = value; this ->size ++; } int getCapacity() { return this->capacity; } int getSize() { return this->size; } private: T * pAddress; int capacity; int size; }; #endif //DAY8_MYARRAY_HPP
2.main.cpp
#include <iostream> #include <cstring> using namespace std; #include "myarray.hpp" class Person { public: Person(){}; Person(string name,int age) { this -> name = name; this -> age = age; } public: string name; int age; }; void myprintint(Myarray<int> & myarray) { for(int i=0;i<myarray.getSize();i++) { cout << myarray[i] << " "; } } void test01() { Myarray<int> myintarray(100); for(int i = 0;i<10;i++) { myintarray.pushback(i+100); } myprintint(myintarray); } void myprintPerson(Myarray<Person> & perArr) { for(int i =0 ; i<perArr.getSize() ; i++) { cout << "name is : " << perArr[i].name << endl << "age is : " << perArr[i].age << endl; } } void test02() { Myarray<Person> myPersonarray(100); Person p1("sunwukong",10); Person p2("zhubajie",20); Person p3("tangseng",30); myPersonarray.pushback(p1); myPersonarray.pushback(p2); myPersonarray.pushback(p3); myprintPerson(myPersonarray); cout << "capacity is " << myPersonarray.getCapacity() << endl; cout << "size is " << myPersonarray.getSize() << endl; } int main() { test01(); test02(); return 0; }
3.结果
/home/lhw/桌面/C++/day8/cmake-build-debug/day8 100 101 102 103 104 105 106 107 108 109 name is : sunwukong age is : 10 name is : zhubajie age is : 20 name is : tangseng age is : 30 capacity is 100 size is 3