c++提供了函數模板(function template.)所謂函數模板,實際上是建立一個通用函數,其函數類型和形參類型不具體制定,用一個虛擬的類型來代表。這個通用函數就成爲函數模板。凡是函數體相同的函數都可以用這個模板代替,不必定義多個函數,只需在模板中定義一次即可。
- c++提供兩種模板機制:函數模板和類模板
目錄
一、函數模板
語法:
template< typename T > T是通用的數據類型
函數名(T&形參1 T&形參2....){ 函數實現 }
1.1 函數模板和普通函數區別
- 函數模板不允許自動類型轉化,必須嚴格匹配類型。
- 普通函數能夠自動進行類型轉化
1.2 函數模板和普通函數在一起調用規則
- c++編譯器優先考慮普通函數
- 如果想強制調用函數模板,需要加 空模板參數列表
- 函數模板可以像普通函數那樣可以被重載
- 如果函數模板可以產生一個更好的匹配(不發生類型轉化),那麼選擇模板
void myPrint(int a, int b)
{
cout << "普通函數myPrint調用" << endl;
}
template<class T>
void myPrint(T a, T b)
{
cout << "函數模板myPrint調用" << endl;
}
template<class T>
void myPrint(T a, T b,T c)
{
cout << "函數模板myPrint(a,b,c)調用" << endl;
}
void test02()
{
//1、如果普通函數和函數模板都可以匹配,優先使用普通函數
int a = 10;
int b = 20;
myPrint(a, b);
//2、如果想強制調用函數模板,需要加 空模板參數列表
myPrint<>(a, b);
//3、函數模板也可以發生函數重載
myPrint(a, b, 10);
//4、如果函數模板可以產生更好的匹配,那麼優先使用函數模板
char c1 = 'c';
char c2 = 'd';
myPrint(c1, c2);
}
二、模板機制剖析
2.1編譯過程
hello.cpp程序是高級c語言程序,這種程序易於被人讀懂。爲了在系統上運行hello.c程序,每一條c語句都必須轉化爲低級的機器指令。然後將這些機器指令打包成可執行目標文件格式,並以二進制形式存儲於磁盤中。
預處理(Pre-processing) -> 編譯(Compiling) ->彙編(Assembling) -> 鏈接(Linking)
2.2 模板實現機制
函數模板機制結論:
- 編譯器並不是把函數模板處理成能夠處理任何類型的函數
- 函數模板通過具體類型產生不同的函數
- 編譯器會對函數模板進行兩次編譯,第一次在聲明的地方對模板代碼本身進行編譯,第二次在調用的地方對參數替換後的代碼進行編譯。
2.3模板的侷限性
函數模板並不是萬能,對於自定義類型數據,有些情況不能夠正常使用
解決方法:可以利用具體化實現
語法:template <> 返回值類型 函數名 (處理數據類型..)
例子:
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
template<class T>
bool myCompare(T&a, T&b)
{
return a == b;
}
//具體語法實現
template<> bool myCompare(Person &a, Person&b)
{
cout << "具體化實現" << endl;
if (a.m_Name == b.m_Name && a.m_Age == b.m_Age)
{
return true;
}
return false;
}
void test02()
{
Person p1("Tom", 10);
Person p2("Tom", 10);
bool ret = myCompare(p1, p2);
if (ret)
{
cout << "p1==p2" << endl;
}
}
三、類模板
類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。
例子:
//可以使用默認參數
template<class NAMETYPE , class AGETYPE = int>
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("Tom", 20); 類模板使用時候,不可以用自動類型推導,只能用顯示指定類型
Person <string>p1("Tom", 20);
p1.showPerson();
}
3.1 類模板做函數參數
可以有3中情況:
- 1、指定傳入類型
- 2、將參數模板化
- 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 doWork(Person <string, int>&p)
{
p.showPerson();
}
void test01()
{
Person <string,int>p1("Tom", 100);
doWork(p1);
}
//2、將參數模板化
template<class T1,class T2>
void doWork2(Person <T1, T2>&p)
{
cout << "T1 = " << typeid(T1).name() << endl;
cout << "T2 = " << typeid(T2).name() << endl;
p.showPerson();
}
void test02()
{
Person <string, int>p1("Jerry", 100);
doWork2(p1);
}
//3、將類模板化
template<class T>
void doWork3(T &p)
{
cout << "T = " << typeid(T).name() << endl;
p.showPerson();
}
void test03()
{
Person <string, int>p1("Bill", 100);
doWork3(p1);
}
3.2 類模板派生普通類
- 子類在繼承時,必須確定出父類的T的數據類型,否則無法給T分配內存空間
//類模板
template<class T>
class MyClass{
public:
MyClass(T property){
this->mProperty = property;
}
public:
T mProperty;
};
//子類實例化的時候需要具體化的父類,子類需要知道父類的具體類型是什麼樣的
//這樣c++編譯器才能知道給子類分配多少內存
//普通派生類
class SubClass : public MyClass<int>{
public:
SubClass(int b) : MyClass<int>(20){
this->mB = b;
}
public:
int mB;
};
3.3 類模板頭文件和源文件分離問題
類模板的聲明和實現放到一個文件中,我們把這個文件命名爲.hpp(這個是個約定的規則,並不是標準,必須這麼寫).
原因:
- 類模板需要二次編譯,在出現模板的地方編譯一次,在調用模板的地方再次編譯。
- C++編譯規則爲獨立編譯。
3.4 模板類碰到友元函數(類內實現)
template<class T1,class T2>
class Person
{
friend void printPerson(Person<T1, T2>& p)
{
cout << "姓名: " << p.m_Name << " 年齡: " << p.m_Age << endl;
}
public:
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
void test01()
{
Person<string, int>p("Tom", 111);
printPerson(p);
}