函數模板
函數模板使用
函數模板注意事項
自動類型推導,必須推導出一致的數據類型T,才可以使用
模板必須要確定出T的數據類型,才可以使用
普通函數和函數模板的類型轉化
普通函數隱式類型轉化(char轉int)
函數模板正常使用不會發生隱式類型轉化【自動推導】
函數模板隱式類型轉化(char轉int),需要指定類型
普通函數與函數模板調用
1. 如果函數模板和普通函數都可以實現,優先調用普通函數
普通函數實現的情況
普通函數只有聲明的情況
2. 可以通過空模板參數列表來強制調用函數模板
3. 函數模板也可以發生重載
4. 如果函數模板可以產生更好的匹配,優先調用函數模板
模板局限性
不能比較構造的類
解決方法:1、運算符重載 2、模板重載
模板重載:
類模板
類模板使用
類模板和函數模板區別
1. 類模板沒有自動類型推導的使用方式
2. 類模板在模板參數列表中可以有默認參數
類模板中成員函數創建時機
類模板對象做函數參數
1. 指定傳入的類型 --- 直接顯示對象的數據類型
2. 參數模板化 --- 將對象中的參數變為模板進行傳遞
3. 整個類模板化 --- 將這個對象類型 模板化進行傳遞
類模板與繼承
指定類型
子類變類模板
類模板成員函數類外實現
類模板分文件編寫
person.h文件:
#pragma once
#include <iostream>
using namespace std;template<class T1, class T2>
class Person {
public:T1 m_Name;T2 m_Age;Person(T1 name, T2 age); //只聲明void show();};
person.cpp文件:
#include "person.h"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>::show()
{cout << this->m_Name << " " << this->m_Age << endl;
}
測試代碼
void test() {Person<string,int> p("張三",2);p.show();
}
解決方式1:直接包含.cpp源文件
解決方式2:將聲明和實現寫到同一個文件中,并更改后綴名為.hpp,hpp是約定的名稱,并不是強制
將person.h文件改為person.hpp文件【約定俗成的后綴,代表類模板】:
#pragma once
#include <iostream>
using namespace std;template<class T1, class T2>
class Person {
public:T1 m_Name;T2 m_Age;Person(T1 name, T2 age); //只聲明void show();};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>::show()
{cout << this->m_Name << " " << this->m_Age << endl;
}
類模板與友元
全局函數類內實現 - 直接在類內聲明友元即可
全局函數類外實現 - 需要提前讓編譯器知道全局函數的存在
類模板案例
屬性設置
private:T* pAddress; //指針指向在堆區開辟的數組起始地址int m_Capacity; //數組容量【總體容量】int m_Size; //數組大小【占有的數據量】
構造函數實現
根據輸入的容量進行初始化
MyArray(int capacity) { //有參構造,根據容量進行初始化this->m_Capacity = capacity;this->m_Size = 0;this->pAddress = new T[this->m_Capacity];}
根據已有的數組進行初始化
//拷貝【防止淺拷貝問題】//構造時拷貝【當參數被聲明為 const 時,函數內部不能修改這個參數的值。有助于防止意外修改傳入的數據,提高代碼的安全性和可維護性。】MyArray(const MyArray& arr) { this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;//深拷貝this->pAddress = new T[arr.m_Capacity];for (int i = 0; i < arr.m_Size; i++)this->pAddress[i] = arr.pAddress[i];}
析構函數實現
~MyArray() { //析構函數,如果堆區不為空,清空,防止出現野指針if (this->pAddress != NULL) {delete[] this->pAddress;}}
重載運算符“=”
//重載運算符“=”,防止出現淺拷貝MyArray& operator=(const MyArray& arr) {//如果已有數據需要先釋放if (this->pAddress != NULL) {delete[] this->pAddress;this->pAddress = NULL;this->m_Capacity = 0;this->m_Size = 0;}this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;//深拷貝this->pAddress = new T[arr.m_Capacity];for (int i = 0; i < arr.m_Size; i++)this->pAddress[i] = arr.pAddress[i];//返回對象本身//【this存放指向該對象的地址,*this解引用即是對象本身,MyArray&代表引用】//【最終效果就是返回對象本身的引用(引用可以理解為別名,使用引用可以訪問該對象)】return *this; }
實現訪問堆區數據
添加數據
//尾插法void Push_Back(const T& val) {//判斷容量是否等于大小【是否數組已經滿了】if (this->m_Capacity == this->m_Size) {return;}//未滿this->pAddress[this->m_Size] = val;this->m_Size++; //更新數組大小}
刪除數據
//尾刪法void Pop_Back() {//如果數組為空if (this->m_Size == 0) {return;}//不為空this->m_Size--; //更新數組大小}
訪問數據
//通過下標訪問數組T& operator[](int index) { //返回引用是為了防止出現arr[i] = 10的賦值情況return this->pAddress[index];}
顯示記錄的參數
//返回數組容量int getCapacity() {return this->m_Capacity;}//返回數組大小int getSize() {return this->m_Size;}
MyArray.hpp總的實現
#pragma once
#include <iostream>
using namespace std;template<class T>
class MyArray {
public:MyArray(int capacity) { //有參構造,根據容量進行初始化this->m_Capacity = capacity;this->m_Size = 0;this->pAddress = new T[this->m_Capacity];}//拷貝【防止淺拷貝問題】MyArray(const MyArray& arr) { //構造時拷貝【當參數被聲明為 const 時,函數內部不能修改這個參數的值。這有助于防止意外修改傳入的數據,提高代碼的安全性和可維護性。】this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;//深拷貝this->pAddress = new T[arr.m_Capacity];for (int i = 0; i < arr.m_Size; i++)this->pAddress[i] = arr.pAddress[i];}//重載運算符“=”,防止出現淺拷貝MyArray& operator=(const MyArray& arr) {//如果已有數據需要先釋放if (this->pAddress != NULL) {delete[] this->pAddress;this->pAddress = NULL;this->m_Capacity = 0;this->m_Size = 0;}this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;//深拷貝this->pAddress = new T[arr.m_Capacity];for (int i = 0; i < arr.m_Size; i++)this->pAddress[i] = arr.pAddress[i];//返回對象本身//【this存放指向該對象的地址,*this解引用即是對象本身,MyArray&代表引用】//【最終效果就是返回對象本身的引用(引用可以理解為別名,使用引用可以訪問該對象)】return *this; }//尾插法void Push_Back(const T& val) {//判斷容量是否等于大小【是否數組已經滿了】if (this->m_Capacity == this->m_Size) {return;}//未滿this->pAddress[this->m_Size] = val;this->m_Size++; //更新數組大小}//尾刪法void Pop_Back() {//如果數組為空if (this->m_Size == 0) {return;}//不為空this->m_Size--; //更新數組大小}//通過下標訪問數組T& operator[](int index) { //返回引用是為了防止出現arr[i] = 10的賦值情況return this->pAddress[index];}//返回數組容量int getCapacity() {return this->m_Capacity;}//返回數組大小int getSize() {return this->m_Size;}~MyArray() { //析構函數,如果堆區不為空,清空,防止出現野指針if (this->pAddress != NULL) {delete[] this->pAddress;}}private:T* pAddress; //指針指向在堆區開辟的數組起始地址int m_Capacity; //數組容量【總體容量】int m_Size; //數組大小【占有的數據量】};
測試代碼
class Person {
public:string m_Name;int m_Age;Person() {};Person(string name, int age){this->m_Name = name;this->m_Age = age;}void show(){cout << this->m_Name << " " << this->m_Age << endl;}};void test() {MyArray<int> arr1(5);//MyArray<int> arr2(arr1);//MyArray<int> arr3(100);//arr3 = arr1;for (int i = 0; i < arr1.getCapacity(); i++)arr1.Push_Back(i);cout << "=====================arr1<int>=======================" << endl;cout << "capacity: " << arr1.getCapacity() << endl;cout << "size " << arr1.getSize() << endl;for (int i = 0; i < arr1.getSize(); i++)cout << arr1[i] << endl;arr1.Pop_Back();cout << "==============arr1尾刪后================" << endl;cout << "capacity: " << arr1.getCapacity() << endl;cout << "size " << arr1.getSize() << endl;for (int i = 0; i < arr1.getSize(); i++)cout << arr1[i] << endl;MyArray<char> arr2(6);for (int i = 0; i < arr2.getCapacity(); i++)arr2.Push_Back(i+97);cout << "====================arr2<char>=========================" << endl;cout << "capacity: " << arr2.getCapacity() << endl;cout << "size " << arr2.getSize() << endl;for (int i = 0; i < arr2.getSize(); i++)cout << arr2[i] << endl;MyArray<Person> arr3(3);for (int i = 0; i < arr3.getCapacity(); i++) {Person p("man"+ std::to_string(i), i);arr3.Push_Back(p);}cout << "====================arr3<Person>=========================" << endl;cout << "capacity: " << arr3.getCapacity() << endl;cout << "size " << arr3.getSize() << endl;for (int i = 0; i < arr3.getSize(); i++)arr3[i].show();}