模板就是建立通用的模具,,大大提高復(fù)用性,。
- 特點:模板不可以直接使用,,它只是一個框架,,它的通用并不是萬能的
- 分類:C++提供兩種模板機制函數(shù)模板和類模板
基礎(chǔ)模板的語法
函數(shù)模板的作用:建立一個通用函數(shù),,其函數(shù)返回值類型和形參類型可以不具體指定,,用一個虛擬類型代表
#include <iostream>
using namespace std;
//針對如下情形
void chang_1(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
void chang_2(double &a, double &b)
{
double temp = a;
a = b;
b = temp;
}
//采用模板完成上面的數(shù)據(jù)交換操作
template<typename T>//聲明一個模板,,告訴編譯器緊跟著的T不要報錯,。typname可以替換成class
//template<class T>
void mychang(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 100;
//模板:自動推導(dǎo)方式,,編譯器自己推導(dǎo)出這個類型
mychang(a, b);//
//模板:直接告訴方式,直接告訴編譯器這個T是什么類型
mychang<int>(a, b);
//模板:
system("pause");
return 0;
}
其中:typname 可以替換成 class template 會直接影響這句話緊跟著的函數(shù),,使其編程模板
注意事項
(1) 自動類型推導(dǎo),,必須推導(dǎo)出一致的數(shù)據(jù)類型T,才能使用,; (2) 模板必須要確定出 T 的數(shù)據(jù)類型,,才可以使用;
template<class T>
void fun()
{
cout <<"模板輸出" << endl;
}
int main()
{
//fun();//錯誤
fun<int>();//隨便指定T為一個類型
}
模板案例——數(shù)組排序
//模板案例——排序
//1,、利用函數(shù)模板封裝一個排序函數(shù),,可以對不同數(shù)據(jù)類型數(shù)組進行排序
//2、排序規(guī)則從大到小,,排序算法為選擇排序
//3,、分別利用的 char 數(shù)組 與 int 數(shù)組進行測試
namespace test
{
//交互函數(shù)模板
template<typename T>
void change_(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
//打印數(shù)組函數(shù)模板
template<typename T>
void printArray(T arr[],int len)
{
for (size_t i = 0; i < len; i++)
{
cout << arr[i]<< " ";
}
cout << endl;
}
//排序模板
template<typename T>
void follow(T arr[],int len)
{
for (size_t i = 0; i < len-1; i++)
{
int max = i;
for (size_t j = i+1; j < len; j++)
{
if (arr[max]<=arr[j])
{
change_(arr[max] , arr[j]);
}
}
}
}
}
int main()
{
//模板:
char charArray[] = "abcidu";
int intArray[] = {1,23,34,56,7,8,98};
int num_char = sizeof(charArray) / sizeof(char);
int num_int = sizeof(intArray) / sizeof(int);
test::follow(charArray, num_char);
test::printArray(charArray, num_char);
test::follow(intArray, num_int);
test::printArray(intArray, num_int);
system("pause");
return 0;
}
普通函數(shù)與函數(shù)模板的區(qū)別
- 普通函數(shù)調(diào)用時可以發(fā)生自動類型轉(zhuǎn)換(隱式類型轉(zhuǎn)換)
- 函數(shù)模板調(diào)用時,如果利用自動類型推導(dǎo),,不會發(fā)生隱式類型轉(zhuǎn)換
- 函數(shù)模板調(diào)用時,,如果利用顯示指定類型方式,可以發(fā)生隱式類型轉(zhuǎn)換
所謂隱式類型轉(zhuǎn)換,,即編譯器自動將char類型變量使用ASCII碼形式進行操作
template<typename T>
void xxx(T a,T b){ }
- (普通函數(shù) 和 xxx(a,c)可以,,xxx(a,c)自動推導(dǎo)就不行)
普通函數(shù)與函數(shù)模板的調(diào)用規(guī)則
- (優(yōu)先級)如果函數(shù)模板和普通函數(shù)都可以實現(xiàn),優(yōu)先調(diào)用普通函數(shù)
namespace test_1
{
void printmy(int a)
{
cout << "普通函數(shù)" << endl;
}
template<typename T>
void printmy(T a)
{
cout << "模板函數(shù)" << endl;
}
}
int main()
{
int a = 10;
test_1::printmy(a);//優(yōu)先代用普通函數(shù)
system("pause");
return 0;
}
輸出:普通函數(shù)
- (強制調(diào)用)可以通過空模板參數(shù)列表來強制調(diào)用函數(shù)模板
//承接上面,,強制調(diào)用模板函數(shù)的方法
test_1::printmy<>(a);
- (模板函數(shù)重載)函數(shù)模板也可以發(fā)生重載
namespace test_1
{
void printmy(int a)
{
cout << "普通函數(shù)" << endl;
}
template<typename T>
void printmy(T a)
{
cout << "模板函數(shù)" << endl;
}
template<typename T>
void printmy(T a ,T b)
{
cout << "重載模板函數(shù)" << endl;
}
}
int main()
{
int a = 10;
test_1::printmy(a);//默認(rèn)調(diào)用普通函數(shù)
test_1::printmy<>(a);//強制調(diào)用模板函數(shù)
test_1::printmy(a, 10);//調(diào)用重載函數(shù)
system("pause");
return 0;
}
- 如果函數(shù)模板可以產(chǎn)生更好的匹配,,優(yōu)先調(diào)用函數(shù)模板
//承接上面
char b = 'x';
test_1::printmy(b);//調(diào)用模板,因為普通函數(shù)需要隱式數(shù)據(jù)轉(zhuǎn)換
模板的局限性
模板并非萬能:
- 如果在給模板傳入的參數(shù)是數(shù)組,,就無法實現(xiàn)了
- 如果傳入的數(shù)據(jù)類型為自定義的數(shù)據(jù)類型(如自定義類),,模板也無法實現(xiàn)
針對上述問題,模板提供的解決方法是:
- 提供模板重載,,可以為這些特定的類型提供具體化的模板
namespace test_2
{
class Animal
{
public:
Animal(string name, int age)
{
this->age_M = age;
this->name_M = name;
}
string name_M;
int age_M;
};
template<typename T>
void campare_animal(T &a,T &b)
{
if (a == b)
{
cout << "歲數(shù)相等" << endl;
}
else
{
cout<<"歲數(shù)不等"<<endl;
}
}
template<> void campare_animal(Animal &a1, Animal &b1)
{
if (a1.age_M==b1.age_M)
{
cout << "歲數(shù)相等" << endl;
}
else
{
cout << "歲數(shù)不等" << endl;
}
}
void Teee()
{
Animal a("cat", 10);
Animal b("dog", 100);
campare_animal(a, b);//這樣做編譯器默認(rèn)重載函數(shù)
}
}
int main()
{
int a = 10;
test_2::Teee();
system("pause");
return 0;
}
類模板的基本語法
類模板的作用: 即:建立一個通用類,,類中的成員 數(shù)據(jù)類型可以不具體制定,用一個虛擬的類型來代表
template<typename T>
類
namespace test_05//類模板語句
{
template<typename NameType,typename AgeType>//在類中用到了多少種不同類型的變量,,就定義多少個模板
class Animal
{
public:
Animal(NameType a, AgeType b)
{
this->age_A = b;
this->name_A = a;
}
NameType name_A;
AgeType age_A;
void test()
{
cout << "姓名:" << this->name_A << " 年齡:" << this->age_A << endl;
}
};
void test_1()
{
Animal<string, int>A_M("cat", 10);
A_M.test();
}
}
int main()
{
test_05::test_1();
system("pause");
return 0;
}
類模板與函數(shù)模板的區(qū)別
template<typename NameType,typename AgeType>//在類中用到了多少種不同類型的變量,,就定義多少個模板
class Animal
{
public:
Animal(NameType a, AgeType b)
{
this->age_A = b;
this->name_A = a;
}
NameType name_A;
AgeType age_A;
void test()
{
cout << "姓名:" << this->name_A << " 年齡:" << this->age_A << endl;
}
};
void test_1()
{
//Animal A_M("cat",10);錯誤!?。,。。。,?!
Animal<string, int>A_M("cat", 10);
A_M.test();
}
- 類模板在模板參數(shù)列表中可以有默認(rèn)參數(shù),(注意函數(shù)模板不能用,,只有類模板才能用)
template<typename NameType,typename AgeType = int>// 默認(rèn)整形int
class Animal
{
public:
Animal(NameType a, AgeType b)
{
this->age_A = b;
this->name_A = a;
}
NameType name_A;
AgeType age_A;
void test()
{
cout << "姓名:" << this->name_A << " 年齡:" << this->age_A << endl;
}
};
void test_1()
{
Animal<string>A_M("cat", 10);//此處的模板列表就可以不用再寫 int 了
A_M.test();
}
類模板中成員函數(shù)創(chuàng)建時機
- 普通類中的成員函數(shù)一開始就可以創(chuàng)建,;
- 類模板中的成員函數(shù)在調(diào)用的時候才會被創(chuàng)建;
class Person1
{
public:
void P_test1()
{
cout <<"person1" <<endl;
}
};
class Person2
{
public:
void P_test2()
{
cout << "person2" << endl;
}
};
template<typename T>//在寫代碼時是不報錯
class test_class
{
public:
T obj;
void test_1()
{
obj.P_test1();
}
void test_2()
{
obj.P_test2();
}
};
void test_A()
{
test_class<Person1>d;//規(guī)定了T的類型
d.test_1();
//d.test_2();//Person1中沒有test_2的成員函數(shù)故報錯
}
int main()
{
test_A();
system("pause");
return 0;
}
如上圖所示,,在編寫程序時編譯器是不會報錯的,,即調(diào)用之前不會創(chuàng)建,只會在調(diào)用時進行創(chuàng)建
類模板對象作函數(shù)參數(shù)
即:類模板實例化出的對象,,向函數(shù)傳參的方式 三種:
- 指定傳入的類型 —— 直接顯示對象的數(shù)據(jù)類型(最常用)
void printAnimal(Animal<string, int> &D)
{
cout << "指定傳入類型" << D.name_A << endl;
}
- 參數(shù)模板化 —— 將對象中的參數(shù)變?yōu)槟0暹M行傳遞
template<typename T1,typename T2>
void printAnimal2(Animal<T1,T2> &D)
{
cout <<"參數(shù)模板化" <<D.name_A<<endl;
cout << "T1類型" << typeid(T1).name() << endl;//查看模板代表的類型
cout << "T2類型" << typeid(T2).name() << endl;
}
- 整個類模板化 —— 將這個對象類型 模板化 進行傳遞
template<typename T>
void printAnimal3(T &p)
{
cout << "整個類模板化"<< endl;
cout << "T" << typeid(T).name() << endl;
}
整體測試:
namespace test_06
{
//類模板對象做函數(shù)參數(shù)
template<typename T1,typename T2>
class Animal
{
public:
Animal(T1 a, T2 b)
{
this->name_A = a;
this->age_A = b;
}
T1 name_A;
T2 age_A;
};
//1,、指定傳入類型
void printAnimal(Animal<string, int> &D)
{
cout << "指定傳入類型" << D.name_A << endl;
}
//2,、參數(shù)模板化
template<typename T1,typename T2>
void printAnimal2(Animal<T1,T2> &D)
{
cout <<"參數(shù)模板化" <<D.name_A<<endl;
cout << "T1類型" << typeid(T1).name() << endl;//查看模板代表的類型
cout << "T2類型" << typeid(T2).name() << endl;
}
//3,、整個類模板化
template<typename T>
void printAnimal3(T &p)
{
cout << "整個類模板化"<< endl;
cout << "T" << typeid(T).name() << endl;
}
void test_AAA()
{
Animal<string, int> A("cat",10);
printAnimal(A);
printAnimal2(A);
printAnimal3(A);
}
}
int main()
{
test_06::test_AAA();
system("pause");
return 0;
}
輸出 指定傳入類型cat 參數(shù)模板化cat T1類型class std::basic_string<char,struct std::char_traits,class std::allocator > T2類型int 整個類模板化 Tclass test_06::Animal<class std::basic_string<char,struct std::char_traits,class std::allocator >,int>
類模板的繼承
- 當(dāng)子類繼承的父類是一類模板時,子類在聲明的時候,,要指定出父類中T的類型
- 如果不指定,,編譯器無法給子類分配內(nèi)存
template<typename T>
class father
{
public:
T M_A;
};
//class Son :public father//錯誤,不指定T類型,,子類無法知道父類中的T應(yīng)該分配多少內(nèi)存
//{
//};
class Son :public father<int>
{
};
- 如果想靈活指定出父類中 T 的類型,,子類也需變?yōu)槟0?/li>
template<typename T>
class father
{
public:
T M_A;
};
template<typename T1,typename T2>//使用模板繼承,這樣就在實例化時才進行內(nèi)存分配
class Son2 :public father<T2>
{
public:
T1 M_B;
};
void test()
{
Son2<string, int>X;//指定了T2為int
}
類模板成員函數(shù)的類外實現(xiàn)
template<typename T1,typename T2>
class Animal
{
public:
Animal(T1 &a,T2 &b);
void test1();
T1 M_A;
T2 M_B;
};
//構(gòu)造函數(shù)模板的類外實現(xiàn)
template<typename T1,typename T2>
Animal<T1, T2>::Animal(T1 &a, T2 &b)
{
this->M_A = a;
this->M_B = b;
}
//成員函數(shù)模板的類外實現(xiàn)
template<typename T1,typename T2>
void Animal<T1, T2>::test1()
{
}
類模板分文件編寫(跨文件編寫)
- 問題:類模板中成員函數(shù)創(chuàng)建時機是在調(diào)用階段,,但是分別文件編寫時鏈接不到原來的類
- 解決 —1:直接包含
.cpp 源文件 在Animal.h
#pragma once
#include <iostream>
using namespace std;
template<typename TA1,typename TA2>
class Animal
{
public:
Animal(TA1 &a, TA2 &b);
void test_A();
TA1 M_A;
TA2 M_B;
};
在 Animal.cpp
#include"Animal.h"
template<typename TA1,typename TA2>
Animal<TA1, TA2>::Animal(TA1 &a,TA2 &b)
{
this->M_A = a;
this->M_B = b;
}
template<typename TA1, typename TA2>
void Animal<TA1, TA2>::test_A()
{
cout << this->M_A << " " << this->M_B << endl;
}
在泛型編程_模板.cpp
#include "Animal.cpp"http://模板需要包含.cpp
int main()
{
int a = 10;
string name_A = "AAA";
Animal<string, int>A(name_A, a);
A.test_A();
system("pause");
return 0;
}
- 解決 — 2(更常用):將聲明和實現(xiàn)寫在同一個文件中,,并更改后綴名為
.hpp ,hpp是約定的名稱,,不是強制 創(chuàng)建一個.hpp 文件專門用于存儲類模板及其成員函數(shù)定義,,在.hpp 中
#pragma once
#include <iostream>
using namespace std;
template<typename TA1,typename TA2>
class Animal
{
public:
Animal(TA1 &a, TA2 &b);
void test_A();
TA1 M_A;
TA2 M_B;
};
template<typename TA1, typename TA2>
Animal<TA1, TA2>::Animal(TA1 &a, TA2 &b)
{
this->M_A = a;
this->M_B = b;
}
template<typename TA1, typename TA2>
void Animal<TA1, TA2>::test_A()
{
cout << this->M_A << " " << this->M_B << endl;
}
在泛型編程_模板.cpp
#include "Animal.hpp"http://模板需要包含.hpp
int main()
{
int a = 10;
string name_A = "AAA";
Animal<string, int>A(name_A, a);
A.test_A();
system("pause");
return 0;
}
類模板與友元(類外與類內(nèi))
- 全局函數(shù)類內(nèi)實現(xiàn):直接在類中聲明友元(相對簡單)
namespace test_08
{
template<typename T1,typename T2>
class Animal
{
friend void printAnimal(Animal<T1, T2> &a) //全局函數(shù)友元函數(shù)的類內(nèi)實現(xiàn)
{
cout << " " << a.M_A<< " " <<a.M_B << endl;
}
public:
Animal(T1 a, T2 b)
{
this->M_A = a;
this->M_B = b;
}
T1 M_A;
T2 M_B;
};
void test()
{
Animal<string, int>A("Derek", 10);
printAnimal(A);
}
}
int main()
{
test_08::test();
system("pause");
return 0;
}
- 全局函數(shù)類外實現(xiàn):需要提前是讓編譯器知道全局函數(shù)的存在(很復(fù)雜)
namespace test_09
{
template<typename T1,typename T2>//先告訴編譯器有Animal模板存在
class Animal;
template<typename T1,typename T2>
void printAnimal(Animal<T1, T2> a)//再告訴編譯器全局函數(shù)的定義,這個必須放在友元聲明前
{
cout << " " << a.M_A << " " << a.M_B << endl;
}
template<typename T1, typename T2>//再進行類模板的內(nèi)部定義
class Animal
{
//全局函數(shù)內(nèi)外實現(xiàn),,需要加空模板列表
friend void printAnimal<>(Animal<T1, T2> a);//在內(nèi)部說明友元聲明,,
public:
Animal(T1 a, T2 b)
{
this->M_A = a;
this->M_B = b;
}
T1 M_A;
T2 M_B;
};
void test()
{
Animal<string, int>A("Derek", 10);
printAnimal(A);
}
}
int main()
{
test_09::test();
system("pause");
return 0;
}
案例——數(shù)組類封裝:
功能:
- 可以對內(nèi)置數(shù)據(jù)類型以及自定義數(shù)據(jù)類型的數(shù)據(jù)進行存儲;
- 將數(shù)組中的數(shù)據(jù)存儲到堆區(qū),;
- 構(gòu)造函數(shù)中可以傳入數(shù)組的容量,;
- 提供對應(yīng)的拷貝構(gòu)造函數(shù)以及operator=防止淺拷貝;
- 提供尾插法和尾刪法對數(shù)據(jù)組中的數(shù)據(jù)進行增加與刪除,;
- 可以通過下標(biāo)的方式訪問數(shù)組中的元素,;
- 可以獲取數(shù)組中當(dāng)前元素個數(shù)和數(shù)組的容量;
在Animal.hpp (預(yù)定俗成的用于寫類模板)
#pragma once
#include<iostream>
using namespace std;
//1,、初始化數(shù)組類
//2,、可以進行類的拷貝;(防止淺拷貝(即堆區(qū)拷貝釋放時出現(xiàn)重復(fù)釋放的情況))
//3、可以使用=進行拷貝;(防止淺拷貝)
template<typename T>
class _Array_
{
friend ostream& operator<<(ostream &cout, _Array_<int> &X);
public:
_Array_(const int &capacity)//初始化構(gòu)造
{
this->m_Capacity = capacity;//初始化時 確定數(shù)組容量
this->m_Size = 0;//初始化數(shù)組內(nèi)元素個數(shù)為0
this->m_address = new T[this->m_Capacity];//容量來決定堆區(qū),如果在使用自定義類型初始化模板時,,這里必須使用默認(rèn)構(gòu)造進行初始化
cout << "普通構(gòu)造" << endl;
}
_Array_(const _Array_ &Array)//拷貝構(gòu)造
{
this->m_Size = Array.m_Size;
this->m_Capacity = Array.m_Capacity;
this->m_address = new T[Array.m_Capacity];//深拷貝操作
for (size_t i = 0; i < Array.m_Size; i++)
{
this->m_address[i] = Array.m_address[i];//拷貝每一個元素
}
cout << "拷貝構(gòu)造" << endl;
}
_Array_& operator=(const _Array_ &Array)//_Array_ a ;_Array_ b ; b = a
{
if (this->m_address != NULL)//堆區(qū)不為空
{
delete[] m_address;
this->m_address = NULL;
}
this->m_Capacity = Array.m_Capacity;
this->m_Size = Array.m_Size;
this->m_address = new T[Array.m_Capacity];//深拷貝操作
for (size_t i = 0; i < Array.m_Size; i++)
{
this->m_address[i] = Array.m_address[i];
}
cout << "重載=" << endl;
return *this;
}
~_Array_()
{
if (this->m_address!=NULL)//堆區(qū)不為空
{
delete[] this->m_address;
this->m_address = NULL;
}
}
//下標(biāo)訪問
T& operator[](const int index)//傳入的是下標(biāo)
{
return this->m_address[index];
}
//尾插法
void push_back(const T &someone)
{
if (this->m_Size < this->m_Capacity)//大小還沒超過容量大小的
{
this->m_address[this->m_Size] = someone;
}
else
{
cout << "容量已滿"<<endl;
return;
}
this->m_Size++;
}
//尾刪法
void pop_back()//邏輯刪除
{
if (this->m_Size == 0)
{
return;
}
else
{
this->m_Size--;
}
}
//獲取大小
int getSize()
{
return this->m_Size;
}
//獲取容量
int getCapacity()
{
return this->m_Capacity;
}
private:
T *m_address;//用這個來管理數(shù)組堆區(qū)
int m_Size;//用于存儲數(shù)組當(dāng)前的大小
int m_Capacity;//用于存儲數(shù)組的整個容量
};
在泛型編程_模板.cpp 注意:如果在使用自定義類型初始化模板時,,默認(rèn)構(gòu)造必須要。
#include"_Array_.hpp"
#include<iostream>
#include<string>
using namespace std;
ostream& operator<<(ostream &cout ,_Array_<int> &X)
{
for (size_t i = 0; i < X.getSize(); i++)
{
cout << X.m_address[i]<< endl;
}
return cout;
}
class Animal
{
public:
Animal(){};//如果在使用自定義類型初始化模板時,,默認(rèn)構(gòu)造必須要,,
Animal(string n, int a)
{
this->name_A = n;
this->age_A = a;
}
string name_A;
int age_A;
};
void test()
{
_Array_<Animal> A(3);
Animal A1("cat", 10);
Animal A2("dog", 10);
Animal A3("bird", 10);
A.push_back(A1);
A.push_back(A2);
A.push_back(A3);
for (size_t i = 0; i < A.getSize(); i++)
{
cout << A[i].name_A<<" : "<<A[i].age_A<< endl;//這里使用重載[]進行操作
}
}
int main()
{
//_Array_<string>P(10);//初始化
//_Array_<string> P2(P);//拷貝構(gòu)造
//P2.operator=(P);
//_Array_<int> P3(10);
//cout << "尾插法:" << endl;
//for (int i = 0; i < 10; i++)
//{
// P3.push_back(i);
//}
//cout << P3 << endl;;
//cout << "大小:" << P3.getSize() << endl;
//cout << "容量:" << P3.getCapacity() << endl;
//cout << "尾刪法:" << endl;
//P3.pop_back();
//cout << P3 << endl;
//cout << "大?。? << P3.getSize() << endl;
//cout << "容量:" << P3.getCapacity() << endl;
test();//自定義類型測試
system("pause");
return 0;
}
|