C++学习日记5——模板
目錄
一、模板的概念
1.1 概念
1.2 特點
二、函數模板
2.1 函數模板
2.2 函數模板注意事項
2.3?函數模板案例
2.4 普通函數與函數模板的區別
2.5 普通函數與函數模板的調用規則
2.6 模板的局限性
三、類模板
3.1 類模板語法
3.2 類模板與函數模板區別
3.3?類模板中成員函數創建時機
3.4 類模板對象做函數參數
3.5 類模板與繼承
3.6 類模板成員函數類外實現
3.7?類模板分文件編寫
3.8 類模板與友元
四、綜合案例
C++ 提高階段:
本階段主要針對 C+ + 泛型編程和 STL 技術做詳細講解,探討 C++ 更深層的使用
一、模板的概念
1.1 概念
模板就是建立通用的模具,大大提高復用性
例如:
(1) 一寸照片模板
(2) PPT 模板
1.2 特點
模板不可以直接使用,它只是個框架
模板的通用并不是萬能的
二、函數模板
C++另一種編程思想稱為泛型編程,主要利用的技術就是模板
C++提供兩種模板機制函數模板和類模板
2.1 函數模板
1、函數模板作用:
建立一個通用函數,其函數返回值類型和形參類型可以不具體制定,用一個虛擬的類型來代表。
?
2、語法:
template<typename T> 函數聲明或定義3、解釋:
template —— 聲明創建模板
typename —— 表面其后面的符號是一種數據類型,可以用 class 代替
T —— 通用的數據類型,名稱可以替換,通常為大寫字母
4、示例:
原來的寫法:
void swapInt(int& a, int& b) {double temp = a;a = b;b = temp; }void swapDouble(double& a, double& b) {double temp = a;a = b;b = temp; }修改后的寫法:
#include<iostream> using namespace std; #include<string>template<typename T>void mySwap(T& a, T& b) {T temp = a;a = b;b = temp; }void test() {int a = 10;int b = 20;// 1、自動類型推導mySwap(a, b);cout << "a = " << a << endl;cout << "b = " << b << endl << endl;// 2、顯示指定類型mySwap<int>(a, b);cout << "a = " << a << endl;cout << "b = " << b << endl; }int main() {test();system("pause");return 0; }5、圖示
?
2.2 函數模板注意事項
自動類型推導,必須推導出一致的數據類型 T 才可以使用
模板必須要確定出 T 的數據類型,才可以使用
#include<iostream> using namespace std; #include<string>template<class T> // typename可以替換成classvoid mySwap(T& a, T& b) {T temp = a;a = b;b = temp; }void test() {int a = 10;int b = 'b';// 錯誤:推導不出一致的T類型mySwap(a, b); }template<class T> void func() {cout << "func調用" << endl; }void test2() {// 注意:必須要加上 <int>func<int>(); }int main() {test();test2();system("pause");return 0; }2.3?函數模板案例
案例描述:
利用函數模板封裝一個排序的函數,可以對不同數據類型數組進行排序
排序規則從大到小,排序算法為選擇排序
分別利用 char 數組和 int 數組進行測試
#include <iostream> using namespace std;//交換的函數模板 template<typename T> void mySwap(T& a, T& b) {T temp = a;a = b;b = temp; }template<class T> // 也可以替換成typename //利用選擇排序,進行對數組從大到小的排序 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) //如果最大數的下標不是i,交換兩者{mySwap(arr[max], arr[i]);}} }template<typename T> void printArray(T arr[], int len) {for (int i = 0; i < len; i++) {cout << arr[i] << " ";}cout << endl; }void test() {// 測試 char 數組char charArr[] = "badcfe";int intArr[] = { 7,5,4,1,9,2,3,6,8 };int num = sizeof(charArr) / sizeof(char);mySort(charArr, num);printArray(charArr, num); }int main() {test();system("pause");return 0; }2.4 普通函數與函數模板的區別
1、普通函數與函數模板區別:
(1) 普通函數調用時可以發生自動類型轉換(隱式類型轉換)
(2) 函數模板調用時,如果利用自動類型推導,不會發生隱式類型轉換
(3) 如果利用顯示指定類型的方式,可以發生隱式類型轉換
#include <iostream> using namespace std;int myAdd01(int a, int b) {return a + b; }template<class T> T myAdd02(T a, T b) {return a + b; }void test() {int a = 10;int b = 20;char c = 'c';cout << myAdd01(a, c) << endl;cout << myAdd02<int>(a, c) << endl; }int main() {test();system("pause");return 0; }2.5 普通函數與函數模板的調用規則
調用規則如下:
(1) 如果函數模板和普通函數都可以實現,優先調用普通函數
(2) 可以通過空模板參數列表來強制調用函數模板
(3) 函數模板也可以發生重載
(4) 如果函數模板可以產生更好的匹配,優先調用函數模板
1、如果函數模板和普通函數都可以實現,優先調用普通函數
#include <iostream> using namespace std;void myPrint(int a, int b) {cout << "調用的是普通函數" << endl; }template<class T> void myPrint(T a, T b) {cout << "調用的模板" << endl; }void test() {int a = 10;int b = 20;myPrint(a, b); }int main() {test();system("pause");return 0; }2、可以通過空模板參數列表來強制調用函數模板
void test() {int a = 10;int b = 20;myPrint<>(a, b); }3、函數模板也可以發生重載
template<class T> void myPrint(T a, T b, T c) {cout << "調用的模板" << endl; }void test() {int a = 10;int b = 20;myPrint(a, b, 100); }2.6 模板的局限性
1、局限性:
模板的通用性并不是萬能的
template<class T> void f(T a, T b) {a = b; }上述代碼中提供的賦值操作,如果傳入的 a 和 b 是一個數組,就無法實現了
template<class T> void f(T a, T b) {if (a > b) { ... } }上述代碼中,如果傳入的數據類型是像 Person 這樣的自定義數據類型,也無法正常運行
2、代碼
#include <iostream> using namespace std; #include <string>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;}else{return false;} }// 1、運算符重載// 2、具體化Person的版本實現代碼 (具體化優先調用) template<> bool myCompare(Person &p1, Person &p2) {if (p1.Name == p2.Name && p1.Age == p2.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", 11);bool ret = myCompare(p1, p2);if (ret){cout << "p1 == p2" << endl;}else{cout << "p1 != p2" << endl;} }int main() {test02();system("pause");return 0; }三、類模板
3.1 類模板語法
1、類模板作用:
過一個通用類,類中的成員數據類型可以不具體制定,用一個虛擬的類型來代表。
2、代碼
#include <iostream> using namespace std; #include <string>template<class NameType, class AgeType> class Person { public:Person(NameType name, AgeType age){this->Name = name;this->Age = age;}void showPerson(){cout << Name << " " << Age << endl;}NameType Name;AgeType Age; };void test() {Person<string, int> p1("孫悟空", 999);p1.showPerson(); }int main() {test();system("pause");return 0; }3.2 類模板與函數模板區別
1、類模板與函數模板區別主要有兩點:
(1) 類模板沒有自動類型推導的使用方式
(2) 類模板在模板參數列表中可以有默認參數
2、代碼
// 1、類模板沒有自動類型推導使用方式 // 即不能使用 Person p1("孫悟空", 999); Person<string, int> p1("孫悟空", 999);// 2、類模板在模板參數列表中可以有默認參數 template<class NameType, class AgeType = int>Person<string> p2("豬八戒", 999);3.3?類模板中成員函數創建時機
1、類模板中成員函數和普通類中成員函數創建時機是有區別的:
(1) 普通類中的成員函數一開始就可以創建
(2) 類模板中的成員函數在調用時才創建
2、代碼:
#include <iostream> using namespace std; #include <string>class Person1 { public:void showPerson1(){cout << "Person1 show" << endl;} };class Person2 { public:void showPerson2(){cout << "Person2 show" << endl;} };template<class T> class MyClass { public:T obj;// 類模板中的成員函數void func1(){obj.showPerson1();}void func2(){obj.showPerson2();} };void test() {MyClass<Person1> m;m.func1();// 下面這個不能運行// m.func2(); }int main() {test();system("pause");return 0; }3、總結:
類模板中的成員函數并不是一開始就創建的, 在調用時才去創建
3.4 類模板對象做函數參數
1、學習目標:
類模板實例化出的對象,向函數傳參的方式
2、共有三種傳入方式:
(1) 指定傳入的類型 ——?直接顯示對象的數據類型
(2) 參數模板化 —— 將對象中的參數變為模板進行傳遞
(3) 整個類模板化 —— 將這個對象類型模板化進行傳遞
3、代碼
#include <iostream> using namespace std; #include <string>template<class NameType, class AgeType> class Person { public:Person(NameType name, AgeType age){this->Name = name;this->Age = age;}void showPerson(){cout << Name << " " << Age << endl;}NameType Name;AgeType Age; };// 1、指定傳入類型 void printPerson1(Person<string, int> &p) {p.showPerson(); }void test1() {Person<string, int> p("孫悟空", 999);printPerson1(p); }// 2、參數模板化 template<class NameType, class AgeType> void printPerson2(Person<NameType, AgeType> &p) {p.showPerson();cout << "NameType 的類型為:" << typeid(NameType).name() << endl;cout << "AgeType 的類型為:" << typeid(AgeType).name() << endl; }void test2() {Person<string, int> p("豬八戒", 999);printPerson2(p); }// 3、整個類模板化 template<class T> void printPerson3(T& p) {p.showPerson();cout << "T 的類型為:" << typeid(T).name() << endl; }void test3() {Person<string, int> p("唐僧", 999);printPerson3(p); }int main() {test1();test2();test3();system("pause");return 0; }4、總結:
通過類模板創建的對象,可以有三種方式向函數中進行傳參
使用比較廣泛是第一種:指定傳入的類型
3.5 類模板與繼承
1、當類模板碰到繼承時,需要注意一下幾點:
(1) 當子類繼承的父類是一 個類模板時,子類在聲明的時候,要指定出父類中T的類型
(2) 如果不指定,編譯器無法給子類分配內存
(3) 如果想靈活指定出父類中T的類型,子類也需變為類模板
2、代碼
#include <iostream> using namespace std;template<class T> class Base { public:T m; };// 方式一 class Son : public Base<int> { };// 方式二:靈活指定父類中 T 類型,子類也需要變類模板 template<class T1, class T2> class Son2 : public Base<T2> {T1 obj; };void test() {Son s1;Son2<int, char> s2; }int main() {test();system("pause");return 0; }3.6 類模板成員函數類外實現
1、學習目標:
能夠掌握類模板中的成員函數類外實現
2、代碼:
#include <iostream> using namespace std; #include <string>template<class NameType, class AgeType> class Person { public:Person(NameType name, AgeType age);void showPerson();NameType Name;AgeType Age; };// 構造函數類外實現 template<class NameType, class AgeType> Person<NameType, AgeType>::Person(NameType name, AgeType age) {this->Name = name;this->Age = age; }// 成員函數類外實現 template<class NameType, class AgeType> void Person<NameType, AgeType>::showPerson() {cout << Name << " " << Age << endl; }void test() {Person<string, int> p("唐僧", 999);p.showPerson(); }int main() {test();system("pause");return 0; }3.7?類模板分文件編寫
方法一:
?
?方法二:
?
3.8 類模板與友元
1、學習目標:
掌握類模板配合友元函數的類內和類外實現
全局函數類內實現——直接在類內聲明友元即可
全局函數類外實現——需要提前讓編譯器知道全局函數的存在
2、代碼
#include <iostream> using namespace std; #include <string>// 提前讓編譯器知道 Person 類存在 template<class NameType, class AgeType> class Person;// 類外實現 template<class NameType, class AgeType> void printPerson2(Person<NameType, AgeType> p) {cout << "類外實現:" << p.Name << " " << p.Age << endl; }template<class NameType, class AgeType> class Person { public:// 全局函數 類內實現friend void printPerson1(Person<NameType, AgeType> p){cout << "姓名:" << p.Name << " " << " 年齡:" << p.Age << endl;}// 全局函數 類外實現// 加空模板的參數列表// 如果全局函數是類外實現,需要讓編譯器提前知道這個函數的存在friend void printPerson2<>(Person<NameType, AgeType> p);Person(NameType name, AgeType age){this->Name = name;this->Age = age;}private:NameType Name;AgeType Age; };// 類內實現測試 void test1() {Person<string, int> p("Tom", 20);printPerson1(p); }// 類外實現測試 void test2() {Person<string, int> p("Tom", 20);printPerson2(p); }int main() {test1();test2();system("pause");return 0; }3、總結:
建議全局函數做類內實現,用法簡單,而且編譯器可以直接識別
四、綜合案例
4.1 案例描述
1、實現一個通用的數組類,要求如下:
可以對內置數據類型以及自定義數據類型的數據進行存儲
將數組中的數據存儲到堆區
構造函數中可以傳入數組的容量
提供對應的拷貝構造函數以及 operator= 防止淺拷貝問題
提供尾插法和尾刪法對數組中的數據進行增加和刪除
可以通過下標的方式訪問數組中的元素
可以獲取數組中當前元素個數和數組的容量
4.2 代碼
1、myArry.h 代碼
2、
總結
以上是生活随笔為你收集整理的C++学习日记5——模板的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: I2C和SPI注定要打一架
- 下一篇: QT常用快捷键