文章目錄
- 一、啥是泛型編程
- 二、函數模板
- 2.1、函數模板的概念
- 2.2、函數模板的格式
- 2.3、函數模板的原理
- 2.4、函數模板的實例化
- 2.4.1、隱式實例化:讓編譯器根據實參推演模板參數的實際類型
- 2.4.2、顯示實例化:在函數名后的<>中指定模板參數的實際類型
- 2.5、函數模板的匹配原則
- 2.5.1、一個非模板函數可以和一個同名的函數模板同時存在,而且該函數模板還可以被實例化為這個非模板函數
- 2.5.2、對于非模板函數和同名的函數模板,如果其他條件都相同,在調用時會優先調用非模板函數,而不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數,那么選擇模板
- 2.5.3、模板函數不允許自動隱式類型轉換,但普通函數可以進行自動隱式類型轉換
- 三、類模板
- 3.1、類模板的定義格式
- 3.2、類模板的實例化
- 四、模板的作用域
一、啥是泛型編程
我們在C++
中不使用模板(泛型),如何實現一個通用的交換函數呢?
void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}
void Swap(double& left, double& right)
{double temp = left;left = right;right = temp;
}
void Swap(char& left, char& right)
{char temp = left;left = right;right = temp;
}......
- 答案也是非常簡單,利用
C++
的函數重載特性,編寫多個類型的Swap
交換函數即可。
但是,這代碼就是咱們程序🐵所說的屎山代碼。因為:
- 重載的多個函數僅僅只是類型不同,代碼的復用率比較低,只要出現新的類型需要交換,就需要新增對應的重載函數。(QAQ)想到不才都忍不住流淚
- 代碼的可維護性比較低,其中一個重載函數出現錯誤可能意味著所有的重載函數都出現了錯誤。
那能否告訴編譯器一個模子,讓編譯器根據不同的類型利用該模子來生成代碼呢?
例如咱們工業生產的時候,是不是只需要生成一個摸具,一壓一和就完成生成產品,不用人工每一個都手打。
那么在C++
中,我們就隆重推出了泛型編程的概念,這也是C++
能在歷朝歷代的語言更替中,依舊呲碴風云的核心之一。
泛型編程:編寫與類型無關的通用代碼,是代碼復用的一種手段。模板是泛型編程的基礎。
二、函數模板
2.1、函數模板的概念
函數模板代表了一個函數家族,該函數模板與類型無關,在使用時被參數化,根據實參類型產生函數的特定類型版本。
2.2、函數模板的格式
template <typename T1,class T2,…,typename Tn>
返回類型 函數名(參數列表)
{//函數體
}
template<typename T>
void Swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;
}
注意:typename
是用來定義模板參數的關鍵字,也可以用class
代替 (切記:不能使用struct
代替class
),其作用都一樣,只是為了編寫模板參數增多時,在代碼可讀性
區分是自定義類型的模板參數還是基礎類型模板參數。
2.3、函數模板的原理
函數模板是一個藍圖,它本身并不是函數。是編譯器產生特定具體類型函數的模具。所以其實模板就是將本來應該我們做的重復的事情交給了編譯器。
在編譯器編譯階段,對于函數模板的使用,編譯器需要根據傳入的實參類型
來推演生成對應類型的函數以供調用。比如,當用int
類型使用函數模板時,編譯器通過對實參類型的推演
,將T
確定為int
類型,然后產生一份專門處理int
類型的代碼,對于double
類型等其他也是如此。(所有類型都可以使用泛型編程,包含自定義類型)
2.4、函數模板的實例化
用不同類型的參數使用模板時,稱為模板的實例化。模板實例化分為:隱式實例化和顯示實例化。
2.4.1、隱式實例化:讓編譯器根據實參推演模板參數的實際類型
#include <iostream>
using namespace std;
template<typename T>
T Add(const T& x, const T& y)
{return x + y;
}
int main()
{int a = 10, b = 20;int c = Add(a, b); //編譯器根據實參a和b推演出模板參數為int類型return 0;
}
- 不才在上面代碼中,只聲明了一個泛型變量
T
- 那么在代碼中沒有顯示的指定泛型類型,那么編譯器就根據實參
a
和b
推演出模板參數為int
類型 - 即:在編譯后,類型
T
就會變為Add()
函數創建一個int Add(const int& x, const int& y)
,那就是在2.3、函數模板的原理
中不才畫的示例圖,不才再次把這個圖拿下來
特別注意:使用模板時,程序猿定義了多少個泛型模板變量,那么編譯器就只會自動識別多少個變量。若超出了泛型模板變量定義的個數就報錯
#include <iostream>
using namespace std;
template<typename T>
T Add(const T& x, const T& y)
{return x + y;
}
int main()
{int a = 10;double dou = 20.2;int c = Add(a, dou); //ERRreturn 0;
}
運行結果如下圖:
- 因為在編譯期間,編譯器根據實參推演模板參數的實際類型時,根據實參
a
將T
推演為int
,根據實參dou
將T
推演為double
,但是模板參數列表中只有一個T
,編譯器無法確定此處應該將T
確定為int
還是double
此時,我們有三種處理方式,第一種就是我們在傳參時將dou
強制轉換為int
類型,第二種就是使用下面說到的顯示實例化,第三種就是改變函數列表或新增函數使得函數重載。
2.4.2、顯示實例化:在函數名后的<>中指定模板參數的實際類型
#include <iostream>
using namespace std;
template<typename T>
T Add(const T& x, const T& y)
{return x + y;
}
int main()
{int a = 10;double b = 1.1;int c = Add<int>(a, b); //指定模板參數的實際類型為intreturn 0;
}
顯示實例化 一般在類模板中的 類成員的實例化中常見
注意:使用顯示實例化時,如果傳入的參數類型與模板參數類型不匹配,編譯器會嘗試進行隱式類型轉換,如果無法轉換成功,則編譯器將會報錯。
2.5、函數模板的匹配原則
2.5.1、一個非模板函數可以和一個同名的函數模板同時存在,而且該函數模板還可以被實例化為這個非模板函數
#include <iostream>
using namespace std;
//專門用于int類型加法的非模板函數
int Add(const int& x, const int& y)
{return x + y;
}
//通用類型加法的函數模板
template<typename T>
T Add(const T& x, const T& y)
{return x + y;
}
int main()
{int a = 10, b = 20;int c = Add(a, b); //調用非模板函數,編譯器不需要實例化int d = Add<int>(a, b); //調用編譯器實例化的Add函數return 0;
}
2.5.2、對于非模板函數和同名的函數模板,如果其他條件都相同,在調用時會優先調用非模板函數,而不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數,那么選擇模板
#include <iostream>
using namespace std;
//專門用于int類型加法的非模板函數
int Add(const int& x, const int& y)
{return x + y;
}
//通用類型加法的函數模板
template<typename T1, typename T2>
T1 Add(const T1& x, const T2& y)
{return x + y;
}
int main()
{int a = Add(10, 20); //與非模板函數完全匹配,不需要函數模板實例化int b = Add(2.2, 2); //函數模板可以生成更加匹配的版本,編譯器會根據實參生成更加匹配的Add函數return 0;
}
2.5.3、模板函數不允許自動隱式類型轉換,但普通函數可以進行自動隱式類型轉換
普通函數可以進行自動隱式類型轉換
int Add(const int& x, const int& y)
{return x + y;
}int main()
{int a = Add(10, 20);int b = Add(2.2, 2); int t = Add(5.5, 5.5); printf("a :> %d\n", a);printf("b :> %d\n", b);printf("t :> %d\n", t);return 0;
}
運行結果:
模板函數不允許自動隱式類型轉換
#include <iostream>
using namespace std;
template<typename T>
T Add(const T& x, const T& y)
{return x + y;
}
int main()
{int a = 10;double dou = 20.2;int c = Add(a, dou); //ERRreturn 0;
}
運行結果如下圖:
三、類模板
3.1、類模板的定義格式
template<class T1, class T2, …, class Tn>
class 類模板名
{//類內成員聲明
};
template<class T>
class Score
{
public:Score(T math, T chinese, T english) :_Math(math), _Chinese(chinese), _English(english) {;}void Print(){cout << "數學:" << _Math << endl;cout << "語文:" << _Chinese << endl;cout << "英語:" << _English << endl;}
private:T _Math;T _Chinese;T _English;
};
注意:類模板中的成員函數/屬性若是放在類外定義時,需要加模板參數列表。(一般使用在靜態方法中)
template<class T>
class Score
{
public:Score(T chinese, T english) :_Chinese(chinese), _English(english) {;}void Print();//為舉例而寫private:static int _Math;T _Chinese;T _English;
};
template <class T>
int Score<T>::_Math = 10;template <class T>
void Score<T>::Print()
{cout << "數學:" << _Math << endl;cout << "語文:" << _Chinese << endl;cout << "英語:" << _English << endl;
}
3.2、類模板的實例化
類模板實例化需要在類模板名字后面根
<>
,然后將實例化的類型放在<>
中即可。
template<class T>
class Score
{
public:Score(T chinese, T english) :_Chinese(chinese), _English(english) {;}void Print();private:static int _Math;T _Chinese;T _English;
};
template <class T>
int Score<T>::_Math = 10;template <class T>
void Score<T>::Print()
{cout << "數學:" << _Math << endl;cout << "語文:" << _Chinese << endl;cout << "英語:" << _English << endl;
}int main() {Score<int> t1( 100, 60);Score<double> t2( 90.5, 2.5);t1.Print();
}
注意:Score
不是真正的類,Score<int>
和Score<double>
才是真正的類。
類模板名字不是真正的類,而實例化的結果才是真正的類。
四、模板的作用域
不論是類模板、函數模板還是類外定義的內容中,
template
其作用域只會最用在下面最近一行中
template<class T>
class Score
{
public:Score(T chinese, T english) :_Chinese(chinese), _English(english) {;}void Print();//為舉例而寫private:static int _Math;T _Chinese;T _English;
};template <class T>
int Score<T>::_Math = 10;
void Score<T>::Print() //err
{cout << "數學:" << _Math << endl;cout << "語文:" << _Chinese << endl;cout << "英語:" << _English << endl;
}
- 在類外第一個定義的
template <class T>
只作用在int Score<T>::_Math = 10;
中,之后就消亡 - 所以在
void Score<T>::Print(){...}
中,T
不再是一個類模板,而是一個未定義的標識符。如下圖:
那為啥我們在類中或函數中還是可以使用模板參數呢?
- 那是因為在
{}
中內容都是屬于該類/函數的內容。
以上就是本章所有內容。若有勘誤請私信不才。萬分感激💖💖 如果對大家有用的話,就請多多為我點贊收藏吧,您的每一個點贊都是不才最大的鼓勵~~~💖💖
ps:表情包來自網絡,侵刪🌹