C++進階-1-模板基礎(函式模板、類模板)
阿新 • • 發佈:2022-05-05
C++進階 模板
1.1 函式模板
1 #include<iostream> 2 using namespace std; 3 4 // 模板 5 6 // 模板的簡單例項 7 // 要求: 8 // 1.利用函式模板封裝一個排序函式,可以對不同資料型別陣列進行排序 9 // 2.排序規則由大到小,排序演算法為:選擇排序 10 // 3.分別利用char陣列和int陣列進行測試 11 12 // 交換函式模板 13 template<class T> 14 void mySwap(T& a, T& b) {15 T temp = a; 16 a = b; 17 b = temp; 18 } 19 20 // 排序演算法模板 21 template<class T> // 也可以寫typename 22 void mySort(T arr[], int len) { 23 // 選擇排序 24 for (int i = 0; i < len; i++) { 25 int max = i; // 認定最大值下標 26 for (int j = i + 1; j < len; j++) { 27// 認定的最大值 比 遍歷出的最大值 要小,說明 j 下標的元素才是真正的最大值 28 if (arr[max] < arr[j]) { 29 max = j; // 更新最大值下標 30 } 31 } 32 if (max != i) { 33 // 交換下標為max和i的元素 34 mySwap(arr[max], arr[i]); 35 } 36 } 37 } 3839 // 列印數字內容模板 40 template<class T> 41 void printArray(T arr[], int len) { 42 for (int i = 0; i < len; i++) { 43 cout << arr[i] << " "; 44 } 45 cout << endl; 46 } 47 48 49 void test01() { 50 51 // 測試陣列 char型別 52 char charArr[] = "badcfe"; 53 int num_charArray = sizeof(charArr) / sizeof(char); 54 mySort(charArr, num_charArray); 55 printArray(charArr, num_charArray); 56 57 // 測試陣列 int型別 58 int intArray[] = { 1, 3, 2, 6, 4, 5, 7 }; 59 int num_int = sizeof(intArray) / sizeof(int); 60 mySort(intArray, num_int); 61 printArray(intArray, num_int); 62 } 63 64 int main() { 65 66 test01(); 67 68 system("pause"); 69 70 return 0; 71 } 72 73 // 總結 74 // C++除了面向物件程式設計思想外,還有 泛型程式設計 以及 STL 技術 75 // 76 // 模板:建立通用的模具,大大提高程式碼的複用性 77 // 模板不可以直接使用,它只是一種框架 78 // 模板的通用並不是萬能的 79 // 80 // 模板分為:函式模板與類模板 81 // 82 // 83 // 函式模板: 84 // 85 // 模板的作用:建立一個通用函式,其函式返回值型別和形參型別都可以不具體制定,用一個虛擬的型別來表示 86 // 語法: 87 // template<typename T> // typename也可以寫成 class 88 // 函式生命或定義 89 // 解釋:T 通用的資料型別,名稱可以替換,通常為大寫字母 90 // 91 // 函式模板的使用方式有兩種:自動型別推導、顯示指定型別 92 // 93 // 模板注意事項: 94 // 1.自動型別推導方式,必須推匯出一致的資料型別T,才可以使用 95 // 2.模板必須要確定出T的資料型別,才可以使用 96 // 97 // 普通函式與函式模板的區別 98 // 1.普通函式呼叫可以發生自動型別轉換(隱式轉換); 99 // 2.函式模板呼叫時,如果利用自動型別推導,不會發生隱式型別轉換 100 // 3.如果利用顯示指定型別的方式,可以發生隱式轉換; 101 // 102 // 所以,建議使用顯示指定型別的方式,呼叫函式模板,因為可以自己確定通用型別 103 // 104 // 普通函式與函式模板的呼叫規則 105 // 1.如果函式模板和普通函式都有可以實現,編譯器會優先呼叫普通函式 106 // 2.可以通過空模板引數列表來強制呼叫函式模板(加<>) 107 // 3.函式模板也可以發生過載 108 // 4.如果利用函式模板可以產生更好的匹配,優先呼叫函式模板 109 // 110 // 所以,既然提供了函式模板,就最好不要再提供普通函數了,否則出現二義性 111 // 112 // 模板的侷限性 113 // 1.模板的通用型不是萬能的 114 // 利用具體化的模板,可以解決自定義型別的通用化 115 // 學習模板不是為了寫模板,而是在STL能夠運用系統提供的模板 116 // 117
1.2 類模板
#include<iostream> #include<string> using namespace std; // 類 模板 // 示例1 //template<class Nametype, class Agetype> // 類模板可以有預設引數 template<class Nametype, class Agetype = int> class Person1 { public: Person1(Nametype name, Agetype age) { this->m_Name = name; this->m_Age = age; } void showPerson() { cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl; } //string m_Name; //int m_Age; Nametype m_Name; Agetype m_Age; }; void test01() { //Person p1("Tom", 19); // 報錯,無法自動推導型別 //Person1<string, int> p1("Tom", 19); Person1<string> p1("Tom", 19); p1.showPerson(); } // 示例2 // 類模板物件做函式引數,三種傳入方式 template<class T1, class T2> class Person2 { public: Person2(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; }; void showPerson() { cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl; } T1 m_Name; T2 m_Age; }; // 1.指定傳入型別 void printPerson1(Person2<string, int>& p) { p.showPerson(); } // 2.引數模板化 template<class T1, class T2> void printPerson2(Person2<T1, T2>& p) { p.showPerson(); // 看一下編譯器推匯出的型別 cout << "T1的型別為:" << typeid(T1).name() << endl; cout << "T2的型別為:" << typeid(T2).name() << endl; } // 3.整個類模板化 template<class T> void printPerson3(T& p) { p.showPerson(); cout << "T的型別為:" << typeid(T).name() << endl; } void test02() { Person2<string, int>p("小明", 20); // 1.指定傳入型別(最常用) printPerson1(p); // 2.引數模板化 printPerson2(p); // 3.整個類模板化 printPerson3(p); } // 示例3 類模板與繼承 template<class T> class Base { T m; }; // class Son:pubic Base // 錯誤,C++編譯需要給子類分配記憶體,必須知道父類中T的型別才可以向下繼承 class Son1 :public Base<int> { // 必須指定一個型別 }; // 如果想靈活制定出父類中T的型別,子類也需要變為模板 template<class T1, class T2> class Son2 :public Base<T2> { public: Son2() { cout << "T1的型別為:" << typeid(T1).name() << endl; cout << "T2的型別為:" << typeid(T2).name() << endl; } T1 obj; }; void test03() { Son1 s1; Son2<int, char> s2; } // 示例4 類模板成員函式類外實現 template<class T1, class T2> class Person4 { public: Person4(T1 name, T2 age); // 類內宣告 //{ // this->m_Name = name; // this->m_Age = age; //}; void showPerson(); // 類內宣告 //{ // cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl; //} T1 m_Name; T2 m_Age; }; // 類外實現 template<class T1, class T2> Person4<T1, T2>::Person4(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; } template<class T1, class T2> void Person4<T1, T2>::showPerson() { cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl; } void test04() { Person4<string, int> p4("張三", 100); p4.showPerson(); } int main() { // 類模板初識 //test01(); // 類模板物件做函式引數 //test02(); // 類模板與繼承 //test03(); // 類模板成員函式類外實現 test04(); system("pause"); return 0; } // 總結 // // 模板分為:函式模板與類模板 // // // 類模板: // // 類模板作用:建立一個通用類,類中的成員 資料型別可以不具體制定,用一個虛擬型別來代表 // // 語法: // template<typename T> // typename可以寫成 class,與typename沒啥具體區別 // 類 // // 類模板與函式模板區別: // 1.類模板沒有自動型別推導的使用方式,所以必須指定 // 2.類模板在模板引數列表中可以有預設引數 // // 類模板中成員函式的建立時機 // 1.普通類中的成員函式一開始就可以建立 // 2.類模板中的成員函式在呼叫時才開始建立 // // 類模板物件做函式引數 // 類模板例項化出物件,向引數傳參 // 三種傳參方式: // 1.制定傳入型別 直接顯示物件的資料型別(最常用) // 2.引數模板化 將物件中的引數變為模板進行傳遞 // 3.整個類模板化 將這個物件型別 模板化進行傳遞 // // 類模板與繼承 // 1.當子類繼承的父類是一個類模板時,子類在宣告的時候,要指定出父類T的型別 // 2.如果不指定,編譯器無法給子類分配記憶體 // 3.如果想靈活制定出父類中T的型別,子類也需要變為模板 // // 類模板的成員函式可以類外實現(類內宣告,類外實現) // 需要新增:1.作用域;2.引數模板/引數模板列表 // // 類模板份檔案編寫 // 問題:類模板中成員函式建立時機是在 呼叫階段 ,導致分檔案編寫時連結不到 // // 解決: // 方式1:直接包含.cpp原始檔 // 方式2:將宣告和實現寫到同一個檔案中,並更改字尾名為.hpp hpp是約定的名稱,並不強制 // // 類模板與友元 // 全域性函式類內實現:直接在類內宣告友元即可(建議使用類內實現) // 全域性函式類外實現:需要提前讓直譯器知道全域性函式的存在 // 1.加一個空模板的引數列表<> // 2.把程式碼剪下到程式碼檔案的最上方,讓編譯器先看到 // 3.加入類的宣告,提前讓編譯器看到模板類 //