目錄
1、模板概念
2、模板特點
3、模板語法
3.1編程思想:泛型編程
3.2兩種模板方法:
3.2.1 函數(shù)模板
3.2.2 類模板
1、模板概念
通用的模具,提高代碼復用性
2、模板特點
不可以直接使用,只是一個框架;模板的通用性并不是萬能的。
3、模板語法
3.1編程思想:泛型編程
3.2兩種模板方法:
3.2.1 函數(shù)模板
函數(shù)模板的作用:建立一個通用函數(shù),其函數(shù)返回值類型和參數(shù)類型可以不具體確定,用一個虛擬的類型來代表。
1)語法:
template<typename T>//函數(shù)聲明或定義
函數(shù)
template——聲明創(chuàng)建函數(shù)模板
typename——表明其后面的符號是一種類型,可以用class替代
T——通用的數(shù)據(jù)類型,名稱可以替換,通常為大寫字母
2)代碼示例:
#include<iostream>
using namespace std;
//傳統(tǒng)方法
//整型兩個整型函數(shù)
void swapInt(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
//交換兩個浮點型
void swapDouble(double& a, double& b)
{
double temp = a;
a = b;
b = temp;
}
//函數(shù)模板
template<typename T>//聲明一個模板,告訴編譯器后面的代碼中緊跟著的T不要報錯,T是一個通用數(shù)據(jù)類型
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
swapInt(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
double c = 30.33;
double d = 40.1;
swapDouble(c, d);
cout << "c = " << c << endl;
cout << "d = " << d << endl;
}
void test02()
{
int a = 10;
int b = 20;
//利用函數(shù)模板交換
//兩種方式使用
//1、自動類型推到
mySwap(a, b);
//2、顯示指定類型
mySwap<int>(a, b);//<>中指定T的類型
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
int main()
{
test02();
system("pause");
return 0;
}
3)總結(jié):
- 函數(shù)模板利用關(guān)鍵字Template
- 使用函數(shù)模板有兩種方法:自動推導類型、顯示指定類型
- 模板的目的是為了提高代碼復用性,將類型參數(shù)化
4)注意事項:
- 自動類型推導,T必須推導出一致的數(shù)據(jù)類型
- 模板必須要確定出T的類型,才可以使用
5)案例-選擇排序
#include<iostream>
using namespace std;
//實現(xiàn)通用的對數(shù)組進行排序的函數(shù)
//規(guī)則 從大到小
//算法 選擇排序
//測試 char 數(shù)組、int數(shù)組
//交換模板
template<typename T>
void mySwap(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
//排序模板
template<typename 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++)
{
//認定的最大值 比 遍歷的j下標要小,說明j下標的元素才是真的最大值
if (arr[max] < arr[j])
{
max = j;
}
}
if (max != i)
{
//交換這兩個元素
mySwap(arr[i], arr[max]);
}
}
}
//打印數(shù)組的模板
template<typename T>//typename和class可相互替換
void printArray(T arr[],int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void test01()
{
char charArr[] = "badcfe";
int len = sizeof(charArr) / sizeof(char)-1;
mySort(charArr, len);
printArray(charArr, len);
}
void test02()
{
int intArr[] = { 1,4,3,6,3,8,2,10 };
int len = sizeof(intArr) / sizeof(int);
mySort(intArr, len);
printArray(intArr, len);
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
6)普通函數(shù)和函數(shù)模板的區(qū)別:普通函數(shù)可以發(fā)生隱式類型轉(zhuǎn)換,但函數(shù)模板只有顯示類型推導時,可以發(fā)生隱式類型轉(zhuǎn)換
#include<iostream>
using namespace std;
//1、普通函數(shù)調(diào)用可以發(fā)生隱式類型轉(zhuǎn)換
//2、函數(shù)模板 用自動類型推導,不可以發(fā)生隱式類型轉(zhuǎn)換
//3、函數(shù)模板 用顯示類型推導,可以發(fā)生隱式類型轉(zhuǎn)換
//普通函數(shù)
int myAdd01(int a, int b)
{
return a + b;
}
template<typename T>
int myAdd02(T a, T b)
{
return a + b;
}
void test01()
{
int a = 10;
int b = 20;
char c = 'c';//把字符型變量轉(zhuǎn)換為整型。ASCII,a-97,c-99,
cout << myAdd01(a, c) << endl;
//cout << myAdd02(a, c) << endl;//自動推導類型!錯誤示例
cout << myAdd02<int>(a, c) << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
?7)普通函數(shù)和函數(shù)模板的調(diào)用規(guī)則:
結(jié)論:如果創(chuàng)建了函數(shù)模板,就別多此一舉創(chuàng)建普通函數(shù)了,OK?
- 如果同時存在普通函數(shù)和模板函數(shù),優(yōu)先調(diào)用普通函數(shù)
- 函數(shù)模板也存在函數(shù)重載
- 可以使用空模板參數(shù)列表? 強制調(diào)用 模板函數(shù)
- 如果調(diào)用模板函數(shù)可以產(chǎn)生更好的效果,優(yōu)先調(diào)用模板函數(shù)
#include<iostream>
using namespace std;
//普通函數(shù)與函數(shù)模板調(diào)用規(guī)則
//1、如果普通函數(shù)和模板函數(shù)都可以調(diào)用,優(yōu)先調(diào)用普通函數(shù)
//2、可以通過空模板參數(shù)列表 強制調(diào)用 函數(shù)模板
//3、函數(shù)模板可以發(fā)生函數(shù)重載
//4、如果函數(shù)模板可以產(chǎn)生更好的匹配,優(yōu)先調(diào)用函數(shù)模板
void myPrint(int a, int b)
{
cout << "調(diào)用普通函數(shù)" << endl;
}
template<class T>
void myPrint(T &a, T &b)
{
cout << "調(diào)用模板" << endl;
}
template<class T>
void myPrint(T a, T b,T c)
{
cout << "調(diào)用 重載模板" << endl;
}
void test01()
{
int a = 10;
int b = 10;
//myPrint(a, b);
通過空模板的模板參數(shù)列表,強制調(diào)用函數(shù)模板
//myPrint<>(a, b);
函數(shù)模板也可以發(fā)生重載
//myPrint<>(a, b,100);
//如果函數(shù)模板產(chǎn)生更好的匹配,優(yōu)先調(diào)用函數(shù)模板
char c1 = 'a';
char c2 = 'c';
myPrint(c1, c2);
}
int main()
{
test01();
system("pause");
return 0;
}
?8)模板的局限性
?模板并不是萬能的,有些特定的數(shù)據(jù)類型,需要具體化方式做特殊實現(xiàn)
#include<iostream>
using namespace std;
//模板的局限性
//模板并不是萬能的,有些特殊數(shù)據(jù)類型,需要具體化方式做特殊實現(xiàn)
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
//對比兩個數(shù)據(jù)是否相等
template<class T>
bool myCompare(T& a, T& b)
{
if (a == b)
{
return true;
}
else
{
return false;
}
}
//利用具體化PersomyComparen的版本,具體化優(yōu)先調(diào)用
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",20);
bool ret = myCompare(p1, p2);
if (ret)
{
cout << "p1 == p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
}
//
int main()
{
//test01();
test02();
system("pause");
return 0;
}
3.2.2 類模板
建立一個通用類,類中成員? 數(shù)據(jù)類型可以不具體制定,用一個虛擬的類型來代替
1)類模板語法
template <typename T>
類
template——聲明創(chuàng)建模板
typename——表明其后面的符號是一種類型,可以用class替代
T——通用的數(shù)據(jù)類型,名稱可以替換,通常為大寫字母
#include<iostream>
using namespace std;
//模板類
template<class NameType,class AgeType>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void ShowPerson()
{
cout << "name: " << this->m_Name << " age: "<<this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01()
{
Person<string, int>p1("Susan", 99);//<string, int>模板參數(shù)列表
p1.ShowPerson();
}
int main()
{
test01();
system("pause");
return 0;
}
2)類模板和函數(shù)模板的區(qū)別
- 類模板沒有自動類型推導的使用方式
- 類模板在模板的參數(shù)列表中,可以有默認參數(shù)
#include<iostream>
using namespace std;
//模板類和函數(shù)模板的區(qū)別
//1、類模板沒有自動類型推導的使用方式
//2、類模板在模板的參數(shù)列表中,可以有默認參數(shù)
//template<class NameType, class AgeType >//沒默認參數(shù)
template<class NameType, class AgeType = int>//類模板可以有默認參數(shù)
class Person
{
public:
Person(NameType name, AgeType age)
{
this->m_name = name;
this->m_age = age;
}
void ShowPerson()
{
cout << "姓名:" << this->m_name << " 年齡:" << this->m_age << endl;
}
NameType m_name;
AgeType m_age;
};
void test01()
{
//Person p1("Susan", 18);//無法自動推導!錯誤示例
//無默認參數(shù)的調(diào)用
Person<string, int> p1("Susan", 18);//只能顯示指定類型,正確示例
p1.ShowPerson();
}
void test02()
{
//有默認參數(shù)的調(diào)用
Person<string> p("Tom", 999);
p.ShowPerson();
}
int main()
{
test02();
system("pause");
return 0;
}
3)類模板中成員函數(shù)的調(diào)用時機
- 普通類中的成員函數(shù)在一開始就可以創(chuàng)建
- 類模板中的成員函數(shù)在調(diào)用時才可以創(chuàng)建
#include<iostream>
using namespace std;
//類模板中的成員函數(shù)的創(chuàng)建時機
//普通類中的成員函數(shù)一開始就可以創(chuàng)建
//類模板中的成員函數(shù)在調(diào)用時才可以創(chuàng)建
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;
//類模板中的成員函數(shù),并不是一開始就創(chuàng)建,而是在模板調(diào)用時再生成
void func1()
{
obj.showPerson1();
}
void func2()
{
obj.showPerson2();
}
};
void test01()
{
MyClass<Person1>m;
m.func1();
//m.func2();//編譯會出錯,說明函數(shù)調(diào)用才會去創(chuàng)建成員函數(shù)
}
int main()
{
test01();
system("pause");
return 0;
}
4)類模板對象做函數(shù)參數(shù)
類模板實例化出的對象,向函數(shù)傳參的方式共三種:
- 指定傳入的類型 ---? 直接顯示對象的數(shù)據(jù)類型
- 參數(shù)模板化? ? ? ? ---將對象的參數(shù)變?yōu)槟0搴筮M行傳遞
- 整個類模板化? ?---將這個對象類型 模板化進行傳遞
#include<iostream>
#include<string>
using namespace std;
//類模板對象做函數(shù)參數(shù)
//1、指定傳入的類型
//2、參數(shù)模板后
//3、整個類模板化
template<class T1,class T2>
class Person
{
public:
Person(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 printPerson(Person<string, int>&p)
{
p.showPerson();
}
void test01()
{
Person<string, int>p("Susan", 18);
printPerson(p);
}
//2、將參數(shù)模板化
template<class T1,class T2>
void printPerson2(Person<T1,T2>&p)
{
p.showPerson();
cout << "T1 的類型:" << typeid(T1).name() << endl;
cout << "T2的類型:" << typeid(T2).name() << endl;
}
void test02()
{
Person<string, int>p("Tom", 98);
printPerson2(p);
}
//3、整個類模板化
template<class T>
void printPerson3(T &p)
{
p.showPerson();
cout << "T的數(shù)據(jù)類型:" << typeid(T).name() << endl;
}
void test03()
{
Person<string, int>p("Lily", 20);
printPerson3(p);
}
int main()
{
test03();
system("pause");
return 0;
}
?總結(jié):指定傳入類型比較常用!
5)類模板與繼承
- 當子類繼承的是父類木板時,子類聲明的時候,需要指定父類模板中T的類型
- 如果不指定,編譯器無法給子類分配內(nèi)存
- 如果想靈活指定父類中T的類型,子類也需要變?yōu)槟0?/li>
#include<iostream>
#include<string>
using namespace std;
//類模板的成員函數(shù)的類外實現(xiàn)
template<class T1,class T2>
class Person
{
public:
Person(T1 name, T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
//構(gòu)造函數(shù)的類外實現(xiàn)
template<class T1,class T2>
Person<T1,T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
//成員函數(shù)的類外實現(xiàn)
template<class T1,class T2>
void Person<T1,T2>::showPerson()//?。?!必須寫模板參數(shù)列表?。?!
{
cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl;
}
void test01()
{
Person<string, int>p("Tom", 100);
p.showPerson();
}
int main()
{
test01();
system("pause");
return 0;
}
注意:成員函數(shù)的類外實現(xiàn)
template<class T1,class T2>
void Person<T1,T2>::showPerson()//?。?!必須寫模板參數(shù)列表!??!?
6)類模板分文件編寫
類模板中的成員函數(shù)創(chuàng)建時機時在調(diào)用階段,導致分文件編寫時鏈接不上
- 解決方法1:直接包含.cpp文件
- 解決方法2:將聲明和實現(xiàn)寫到同一個文件中,并改后綴名為.hpp,hpp是約定的名稱,并不強制
person1.hpp文件
#pragma once
#include<iostream>
using namespace std;
template<class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
void showPerson();
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;
}
13模板-類模板分文件編寫.cpp
#include<iostream>
#include<string>
using namespace std;
//第一種解決方式
#include"person.cpp"http://#include"person.h"改為#include"person.cpp"才不會報錯
//第二種解決方式 將.h和.cpp文件中的內(nèi)容寫到一起,將后綴名改為.hpp文件
#include"person1.hpp"
//類模板的份文件編寫
//類模板中的成員函數(shù)創(chuàng)建時機在調(diào)用階段,導致分文件時鏈接不是
//
//template<class T1, class T2>
//class Person
//{
//public:
// Person(T1 name, T2 age);
//
// void showPerson();
//
// 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("Susan", 19);
p.showPerson();
}
int main()
{
test01();
system("pause");
return 0;
}
7)類模板和友元:建議使用全局函數(shù)的類內(nèi)實現(xiàn)
#include<iostream>
#include<string>
using namespace std;
//類模板和友元
// 通過全局函數(shù)打印Person的信息
//提前讓編譯器知道Person類的存在
template<class T1,class T2>
class Person;
//全局函數(shù) 類外實現(xiàn)
template<class T1, class T2>
void printPerson2(Person<T1, T2> p)
{
cout << "類外實現(xiàn)——Name: " << p.m_Name << " Age:" << p.m_Age << endl;
}
template<class T1,class T2>
class Person
{
//全局函數(shù) 類內(nèi)實現(xiàn)
friend void printPerson(Person<T1, T2> p)
{
cout << "Name: " << p.m_Name << " Age:" << p.m_Age << endl;
}
//全局函數(shù) 類外實現(xiàn)
//加空模板的參數(shù)列表
//如果成員函數(shù)類外實現(xiàn),需要讓編譯器提前知道這個函數(shù)的存在
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;
};
//類內(nèi)實現(xiàn)測試
void test01()
{
Person<string, int>p("Tom",10);
printPerson(p);
}
//類外實現(xiàn)測試
void test02()
{
Person<string, int>p("Susan", 2);
printPerson2(p);
}
int main()
{
//test01();
test02();
system(+"pause");
return 0;
}
8)案例:通用的數(shù)組類
- 運用知識:類模板、深淺拷貝、函數(shù)重載
- 幾個重點:
T* = new T[5];//new出來地址所以用地址變量承接,new的類型是T,所以指針數(shù)據(jù)類型也是T
”拷貝構(gòu)造和operator=“防止在堆區(qū)數(shù)據(jù)淺拷貝帶來的問題
T&?operator[](int index)//函數(shù)調(diào)用作為左值,要反回引用&文章來源:http://www.zghlxwxcb.cn/news/detail-410107.html
- 代碼實現(xiàn):
文章來源地址http://www.zghlxwxcb.cn/news/detail-410107.html
#pragma once
//自己通用的數(shù)組類
#include<iostream>
using namespace std;
template<class T>
class MyArray
{
public:
//有參構(gòu)造 參數(shù) 容量
MyArray(int capacity)
{
//cout << "MyArray 的有參構(gòu)造調(diào)用" << endl;
this->m_Capacity = capacity;
this->m_Size = 0;
this->pAddress = new T[capacity];
}
MyArray(const MyArray& arr)//拷貝構(gòu)造 防止淺拷貝問題
{
//cout << "MyArray 的拷貝構(gòu)造調(diào)用" << endl;
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
//this->pAddress = arr.pAddress;//淺拷貝導致堆區(qū)開辟空間重復釋放
//深拷貝
this->pAddress = new T[arr.m_Capacity];
//將arr中的數(shù)據(jù)拷貝過來了
for (int i = 0; i < this->m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
}
//operater= 防止淺拷貝問題
MyArray& operator=(const MyArray& arr)
{
//cout << "MyArray 的operater=調(diào)用" << endl;
//先判斷原來堆區(qū)是否有數(shù)據(jù),有就先釋放
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
this->m_Size = 0;
this->m_Capacity = 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_Capacity; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
return* this;
}
//尾增
void Push_Back(const T& val)
{
//判斷容量是否等于大小
if (this->m_Capacity == this->m_Size)
{
return;
}
this->pAddress[this->m_Size] = val;//數(shù)組末尾插入數(shù)據(jù)
this->m_Size++;//更新數(shù)組大小
}
//尾刪
void Pop_Back()
{
//讓用戶訪問不到最后一個元素,即為尾刪,邏輯刪除
if (this->m_Size == 0)
{
return;
}
this->m_Size--;
}
//用戶通過下標訪問數(shù)組中元素 arr[0]=100;
T& operator[](int index)//函數(shù)調(diào)用作為左值,要反回引用&
{
return this->pAddress[index];
}
//返回數(shù)組容量
int getCapacity()
{
return this->m_Capacity;
}
//返回數(shù)組大小
int getSize()
{
return this->m_Size;
}
//析構(gòu)函數(shù) 堆區(qū)數(shù)據(jù)清空
~MyArray()
{
//cout << "MyArray 的析構(gòu)函數(shù)調(diào)用" << endl;
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;//指針置為空
}
}
private:
T* pAddress;//指針指向堆區(qū)開辟的真實數(shù)組
int m_Capacity;//數(shù)組容量
int m_Size;
};
到了這里,關(guān)于c++學習筆記-提高編程-模板(嗶站-黑馬程序員c++教學視頻)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!