C++模板 —— 万字带你了解C++模板(蓝桥杯算法比赛必备知识STL基础)
目錄
?
模板的概念
函數模板?
?????????函數模板的作用:
函數模板的語法:
解釋:
示例:
總結:
函數模板注意事項
注意事項:
示例:
總結:
?函數模板案例
案例描述:
示例:
普通函數與函數模板的區(qū)別
總結:
普通函數與函數模板的調用規(guī)則
調用規(guī)則如下:
示例:
模板的局限性(函數模板的第二種實現)
類模板
類模板基礎語法?
語法:
解釋:
示例:
類模板與函數模板的區(qū)別
問題引出(一)
問題引出(二)
類模板中成員函數調用時機
添加測試代碼:
總結:
類模板對象做函數參數
類模板與繼承
示例:
類模板成員函數類外實現
類模板與友元
模板的概念
模板就是我們建立的通用的模具,用來提高代碼的復用性。
生活中最經典的就是證件照模板了
?上面模板的特點:
-
模板不可以直接使用,它只是一個框架(不可能拿一個照片模板交給領導或者老師吧,得有你自己的“信息”)
-
模板的通用并不是萬能的
函數模板?
- ?C++的另一種編程思想——泛型編程,利用的主要就是模板。
- C++提供兩種模板機制:函數模板和類模板
函數模板的作用:
? ? ? ? 建立一個通用函數,其函數返回值類型和形參類型不具制定,用一個虛擬類型代替。
函數模板的語法:
//template告訴編譯器,要開始寫模板了 //typename或者class都可以 //T是虛擬類型 template<typename T> 函數聲明或定義解釋:
template --- 聲明創(chuàng)建模板
typename --- 表面其后面的符號是一種數據類型,可以用class代替
T --- 通用的數據類型,名稱可以替換,通常為大寫字母
示例:
//交換整型函數 void swapInt(int &a,int &b){int temp = 0;a = b;b = temp; }//交換浮點型函數 void swapDouble(double &a,double &b){double temp = a;a = b;b = temp; }//利用函數模板提供通用的交換函數 template<tyoename T> void mySwap(T &A,T &b) {T temp = a;a = b;b = temp; }void test01() {int a = 10;int b = 20;//swapInt(a, b);//利用模板實現交換//1、自動類型推導mySwap(a, b);//2、顯示指定類型mySwap<int>(a, b);cout << "a = " << a << endl;cout << "b = " << b << endl;}int main() {test01();system("pause");return 0; }總結:
-
函數模板利用關鍵字 template
-
使用函數模板有兩種方式:自動類型推導、顯示指定類型
-
模板的目的是為了提高復用性,將類型參數化
函數模板注意事項
注意事項:
-
自動類型推導,必須推導出一致的數據類型T,才可以使用
-
模板必須要確定出T的數據類型,才可以使用(函數中沒有用到T的話,就只能自己寫了)
示例:
//利用模板提供通用的交換函數 template<class T> //typename可以替換成class void mySwap(T& a, T& b) {T temp = a;a = b;b = temp; }// 1、自動類型推導,必須推導出一致的數據類型T,才可以使用 void test01() {int a = 10;int b = 20;char c = 'c';mySwap(a, b); // 正確,可以推導出一致的T//mySwap(a, c); // 錯誤,推導不出一致的T類型 }// 2、模板必須要確定出T的數據類型,才可以使用 template<class T> void func() {cout << "func 調用" << endl; }void test02() {//因為函數體中沒有用到T,所以自動類型推導也不知道怎么推導了//func(); //錯誤,模板不能獨立使用,必須確定出T的類型func<int>(); //利用顯示指定類型的方式,給T一個類型,才可以使用該模板 }int main() {test01();test02();system("pause");return 0; }總結:
-
使用模板時必須確定出通用數據類型T,并且能夠推導出一致的類型
?函數模板案例
案例描述:
-
利用函數模板封裝一個排序的函數,可以對不同數據類型數組進行排序
-
排序規(guī)則從大到小,排序算法為選擇排序
-
分別利用char數組和int數組進行測試
示例:
//交換函數模板 template<class T> void mySwap(T& a, T& b) {T temp = a;a = b;b = temp; }//排序算法 template<class T> void mySort(T 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 (max != i){//交換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 test01() {//測試char數組char charArr[] = "bcfac";int num = sizeof(charArr) / sizeof(char);mySort(charArr, num);printArray(charArr , num); }void test02() {//測試int數組int intArr[] = { 7, 5, 8, 1, 3, 9, 2, 4, 6 };int num = sizeof(intArr) / sizeof(int);mySort(intArr, num);printArray(intArr, num); }int main() {test01();test02();system("pause");return 0; }如果上面這些程序你都實現了,那么你一定會對模板深有體會,模板大大提高了代碼復用,需要熟練掌握
普通函數與函數模板的區(qū)別
普通函數與函數模板區(qū)別:
-
普通函數調用時可以發(fā)生自動類型轉換(隱式類型轉換)
-
函數模板調用時,如果利用自動類型推導,不會發(fā)生隱式類型轉換
-
如果利用顯示指定類型的方式,可以發(fā)生隱式類型轉換
總結:
????????建議使用顯示指定類型的方式,調用函數模板,因為可以自己確定通用類型T
普通函數與函數模板的調用規(guī)則
調用規(guī)則如下:
如果函數模板和普通函數都可以實現,優(yōu)先調用普通函數
可以通過空模板參數列表來強制調用函數模板
函數模板也可以發(fā)生重載
如果函數模板可以產生更好的匹配,優(yōu)先調用函數模板
簡單來說就是,函數模板里面,同名普通函數(只有聲明也成立,只是會報錯🤣)優(yōu)先調用,同時函數模板也是可以重載的。
示例:
//普通函數與函數模板調用規(guī)則 void myPrint(int a, int b) {cout << "調用的普通函數" << endl; }template<typename T> void myPrint(T a, T b) { cout << "調用的模板" << endl;//函數模板重載 template<typename T> void myPrint(T a, T b, T c) { cout << "調用重載的模板" << endl; }void test01() {//1、如果函數模板和普通函數都可以實現,優(yōu)先調用普通函數// 注意 如果告訴編譯器 普通函數是有的,但只是聲明沒有實現,或者不在當前文件內實現,就會報錯找不到int a = 10;int b = 20;myPrint(a, b); //調用普通函數//2、可以通過空模板參數列表來強制調用函數模板myPrint<>(a, b); //調用函數模板//3、函數模板也可以發(fā)生重載int c = 30;myPrint(a, b, c); //調用重載的函數模板//4、 如果函數模板可以產生更好的匹配,優(yōu)先調用函數模板char c1 = 'a';char c2 = 'b';myPrint(c1, c2); //調用函數模板 }int main() {test01();system("pause");return 0; }其實我們提供函數模板了,就盡量不要再寫普通函數了,不然會出現二義性的。
模板的局限性(函數模板的第二種實現)
函數模板并不是萬能的,有些特定數據類型,需要用具體化方式做特殊實現。
只有C++內置的數據類型可以直接使用前面學到的函數模板,自定義數據類型往往不能實現。
#include<iostream> using namespace std;#include <string>class Person { public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age; };//普通函數模板 template<class T> bool myCompare(T& a, T& b) {if (a == b){return true;}else{return false;} }//具體化,顯示具體化的原型和定意思以template<>開頭,并通過名稱來指出類型 //具體化優(yōu)先于常規(guī)模板 template<> bool myCompare(Person &p1, Person &p2) {if ( p1.m_Name == p2.m_Name && p1.m_Age == p2.m_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);//自定義數據類型,不會調用普通的函數模板//可以創(chuàng)建具體化的Person數據類型的模板,用于特殊處理這個類型bool ret = myCompare(p1, p2);if (ret){cout << "p1 == p2 " << endl;}else{cout << "p1 != p2 " << endl;} }int main() {test01();test02();system("pause");return 0; }如上,利用具體化的模板,可以解決自定義類型的通用化。
類模板
類模板和函數模板的區(qū)別在于模板聲明下面加的是類還是函數。
類模板基礎語法?
語法:
template<typename T> 類解釋:
template --- 聲明創(chuàng)建模板
typename --- 表面其后面的符號是一種數據類型,可以用class代替
T --- 通用的數據類型,名稱可以替換,通常為大寫字母
示例:
//類模板 // 流程://class后面緊跟著的就是通用數據類型(如果成員中需要兩個,就用逗號分隔寫兩個)//然后test01()中,在傳入的時候,用模板參數列表給這里的兩個..Type傳值//后面的兩個數據"猴哥",500;是給有參構造里面的name和age傳值//最后調用showPerson輸出 template<class NameType,class AgeType> class Person { public://寫構造函數賦初值Person(NameType name, AgeType age){this->m_Age = age;this->m_Name = name;}void showPerson(){cout << "name:" << this->m_Name << "age:" << this->m_Age << endl;}//兩個類型不一樣,需要兩個模板數據類型NameType m_Name;AgeType m_Age; };void test01() { //將類型參數化Person<string, int> p("猴哥",500);//后兩個數據是實參,傳給有參構造p.showPerson(); }int main() {test01();system("pause");return 0; }類模板與函數模板的區(qū)別
問題引出(一)
現在我們對上節(jié)示例稍微進行一下改動,把我們的顯示類型聲明給去掉。報錯
所以類模板是沒有自動類型推導的。
問題引出(二)
看這里,我沒有聲明傳入int型,但是也能實現,你猜是為什么?
?
?原來,我在前面加了點“小料”。
我在聲明模板的時候直接把AgeTyoe = int了,后面?zhèn)鬟M去的時候就不需要說是int型了。
所以這就是類模板在模板參數列表中可以有默認參數。
類模板中成員函數調用時機
//類模板中成員函數創(chuàng)建時機 //類模板中成員函數在調用時才去創(chuàng)建 class Person1 { public:void showPerson1() {cout << "showPerson1函數調用" << endl;} }; class Person2 { public:void showPerson2() {cout << "showPerson2函數調用" << endl;} };//類模板 template<class T> class MyClass { public:T obj;//類模板中的成員函數void func1() {obj.showPerson1();}void func2() {obj.showPerson2();} };int main() {system("pause");return 0; }這里沒有調用類模板里面的成員函數,運行發(fā)現成功了。
這兩個成員函數fuc1,func2只要不調用就不會創(chuàng)建,因為編譯器不知道類模板里面的obj是什么類型的。
添加測試代碼:
void test01() {MyClass<Person1>m;m.func1();m.func2(); }調用測試運行結果:
我們調用成員函數func1,func2,就可以確定obj就是Person1的數據類型,Person1沒有showPerson2成員函數,所以會報錯。
總結:
????????類模板中的成員函數并不是一開始就創(chuàng)建的,在調用時才去創(chuàng)建
類模板對象做函數參數
一共有三種傳入方式:
指定傳入的類型 --- 直接顯示對象的數據類型
參數模板化 --- 將對象中的參數變?yōu)槟0暹M行傳遞
整個類模板化 --- 將這個對象類型 模板化進行傳遞
類模板與繼承
當類模板碰到繼承時,需要注意一下幾點:
-
當子類繼承的父類是一個類模板時,子類在聲明的時候,要指定出父類中T的類型
-
如果不指定,編譯器無法給子類分配內存
-
如果想靈活指定出父類中T的類型,子類也需變?yōu)轭惸0?/p>
示例:
template<class T> class Base {T m; };//class Son:public Base //錯誤,c++編譯需要給子類分配內存,必須知道父類中T的類型才可以向下繼承 class Son :public Base<int> //必須指定一個類型 { }; void test01() {Son c; }//類模板繼承類模板 ,可以用T2指定父類中的T類型 template<class T1, class T2>//想要靈活的指定父類中的T類型,子類也需要變成類模板 class Son2 :public Base<T2> { public:Son2(){cout << typeid(T1).name() << endl;cout << typeid(T2).name() << endl;} };void test02() {Son2<int, char> child1; }int main() {test01();test02();system("pause");return 0; }類模板成員函數類外實現
類模板中成員函數類外實現時,需要加上模板參數列表。?
#include <string>template<class T1, class T2> class Person { public://成員函數類內聲明Person(T1 name, T2 age);void showPerson();public:T1 m_Name;T2 m_Age; };//構造函數 類外實現 template<class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age) {this->m_Name = name;this->m_Age = age; }//成員函數 類外實現 template<class T1, class T2> void Person<T1, T2>::showPerson() {cout << "姓名: " << this->m_Name << " 年齡:" << this->m_Age << endl; }void test01() {Person<string, int> p("Tom", 20);p.showPerson(); }int main() {test01();system("pause");return 0; }類模板與友元
全局函數類內實現 - 直接在類內聲明友元即可
全局函數類外實現 - 需要提前讓編譯器知道全局函數的存在
#include <string>//2、全局函數配合友元 類外實現 - 先做函數模板聲明,下方在做函數模板定義,在做友元 template<class T1, class T2> class Person;//如果聲明了函數模板,可以將實現寫到后面,否則需要將實現體寫到類的前面讓編譯器提前看到 //template<class T1, class T2> void printPerson2(Person<T1, T2> & p); template<class T1, class T2> void printPerson2(Person<T1, T2> & p) {cout << "類外實現 ---- 姓名: " << p.m_Name << " 年齡:" << p.m_Age << endl; }template<class T1, class T2> class Person {//1、全局函數配合友元 類內實現friend void printPerson(Person<T1, T2> & p){cout << "姓名: " << p.m_Name << " 年齡:" << p.m_Age << endl;}//全局函數配合友元 類外實現friend void printPerson2<>(Person<T1, T2> & p);public:Person(T1 name, T2 age){this->m_Name = name;this->m_Age = age;}private:T1 m_Name;T2 m_Age;};//1、全局函數在類內實現 void test01() {Person <string, int >p("Tom", 20);printPerson(p); }//2、全局函數在類外實現 void test02() {Person <string, int >p("Jerry", 30);printPerson2(p); }int main() {//test01();test02();system("pause");return 0; }總結
以上是生活随笔為你收集整理的C++模板 —— 万字带你了解C++模板(蓝桥杯算法比赛必备知识STL基础)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (转)熊绎:我看软件工程师的职业规划
- 下一篇: SQL Server数据库的管理及维护