1.定義
它是一種泛化的編程方式,其實(shí)現(xiàn)原理為程序員編寫(xiě)一個(gè)函數(shù)/類(lèi)的代碼示例,讓編譯器去填補(bǔ)出不同的函數(shù)實(shí)現(xiàn)。允許您延遲編寫(xiě)類(lèi)或方法中的編程元素的數(shù)據(jù)類(lèi)型的規(guī)范,直到實(shí)際在程序中使用它的時(shí)候。換句話(huà)說(shuō),泛型允許您編寫(xiě)一個(gè)可以與任何數(shù)據(jù)類(lèi)型一起工作的類(lèi)或方法。
2.模板
模板是泛型編程的基礎(chǔ),是創(chuàng)建泛型類(lèi)或函數(shù)的藍(lán)圖或公式。庫(kù)容器,比如迭代器和算法,都是泛型編程的例子,它們都使用了模板的概念。每個(gè)容器都有一個(gè)單一的定義,比如 向量,我們可以定義許多不同類(lèi)型的向量,比如 vector 或 vector 。可以使用模板來(lái)定義函數(shù)和類(lèi),我們來(lái)具體分析一下模板函數(shù)和模板類(lèi)的創(chuàng)建和使用:
2.1 模板函數(shù)
我們想實(shí)現(xiàn)像Python一樣,一個(gè)帶有參數(shù)的方法,它的相同參數(shù)可以傳遞不同類(lèi)型的值。我們通過(guò)下面的例子來(lái)了解一下:
#include<iostream>
using namespace std;
template<typename T>
void Test(T& arg1,T& arg2) // 這是一個(gè)實(shí)現(xiàn)兩個(gè)變量值交換的函數(shù)
{
T temp = arg1;
arg1 = arg2;
arg2 = temp;
}
// typename 是定義模板的關(guān)鍵字,可以用class來(lái)替代(注意不能用struct)
int main()
{
int a = 10, b = 20;
double c = 5.2, d = 10.5;
Test(a, b);
Test(c, d);
cout << a << " " << b << endl;
cout << c << " " << d << endl;
}
// 輸出結(jié)果:
20 10
10.5 5.2
// 我們交換了int類(lèi)型的a與b的值,double類(lèi)型c和d的值
如果我們將int和double同時(shí)傳給Swap這個(gè)函數(shù),那么編譯器會(huì)報(bào)錯(cuò),表示模板參數(shù)T不明確,那么我們需要做如下改動(dòng):
(1)把函數(shù)傳參中的引用去掉
(2)把a(bǔ)強(qiáng)制轉(zhuǎn)換成double類(lèi)型,Swap((double)a, c)
#include<iostream>
using namespace std;
template<typename T>
void Test(T arg1,T arg2) // 把“&”引用去掉
{
T temp = arg1;
arg1 = arg2;
arg2 = temp;
cout << arg1 << " " << arg2 << endl;
}
int main()
{
int a = 10;
double c = 5.2;
Test((double)a, c);
cout << "a:" << a << " c:" << c << endl;
}
// 輸出結(jié)果:
5.2 10
a:10 c:5.2
// a,c值沒(méi)有變,是因?yàn)槲覀儌鲄⑹侵祩鬟f
接下來(lái)我們看一下,多個(gè)模板參數(shù)的情況:
#include<iostream>
#include<typeinfo>
using namespace std;
template<typename T1, typename T2>
void Info(T1 arg1,T2 arg2)
{
cout << typeid(arg1).name() << endl;
cout << typeid(arg2).name() << endl;
}
int main()
{
int a = 10;
double c = 5.2;
Info(a, c);
cout << "a:" << a << " c:" << c << endl;
}
// 輸出結(jié)果:
i
d
a:10 c:5.2
可以看到,實(shí)際上函數(shù)在調(diào)用這個(gè)模板的時(shí)候,已經(jīng)實(shí)例化了這個(gè)函數(shù)(即替換模板參數(shù)為正確參數(shù)類(lèi)型)這時(shí)候在后臺(tái)處理的時(shí)候,其實(shí)Show函數(shù)已經(jīng)實(shí)例化為了下面這個(gè)樣子
void Info(int arg1,double arg2)
{
cout << typeid(arg1).name() << endl;
cout << typeid(arg2).name() << endl;
}
2.2 函數(shù)模板實(shí)例化
上面的方式,是編譯器自動(dòng)幫我們實(shí)例化模板參數(shù)。在實(shí)際使用中,我們還可以自己指定實(shí)例化為什么類(lèi)型
- 利用強(qiáng)制類(lèi)型轉(zhuǎn)換
- 使用
<int>
直接指定實(shí)例化為int類(lèi)型
#include<iostream>
using namespace std;
template<typename T>
void Test(T arg1,T arg2) // 把“&”引用去掉
{
T temp = arg1;
arg1 = arg2;
arg2 = temp;
cout << arg1 << " " << arg2 << endl;
}
int main()
{
int a = 10;
double c = 5.2;
Test((double)a, c); // 強(qiáng)制類(lèi)型轉(zhuǎn)換
Test<int>(a, c); // 直接指定
cout << "a:" << a << " c:" << c << endl;
}
/*
使用第二種方式的時(shí)候,編譯器會(huì)對(duì)另外一個(gè)不匹配的參數(shù)進(jìn)行隱式類(lèi)型轉(zhuǎn)換。如果轉(zhuǎn)換不成功,則會(huì)報(bào)錯(cuò)。
另外注意的是,函數(shù)模板參數(shù)T同樣可以用來(lái)作為返回值,但是不能通過(guò)返回值來(lái)推斷參數(shù)T的類(lèi)型。比如下面這個(gè)函數(shù),我們?cè)谑褂玫臅r(shí)候就需要直接指定模板參數(shù)T,而不能寫(xiě)一個(gè)int* ptr=test(10)讓編譯器通過(guò)“返回值是int*接收的,所以函數(shù)模板參數(shù)T是int”來(lái)推斷。
*/
2.3 函數(shù)模板實(shí)例化
函數(shù)模板支持給予參數(shù)缺省值,當(dāng)一個(gè)參數(shù)不確定的時(shí)候,函數(shù)模板是支持給予缺省值的
template<typename T=char>
T* Test(int num)
{
return new T[num];
}
當(dāng)有多個(gè)模板參數(shù)時(shí),缺省值需要從右往左給,當(dāng)然函數(shù)模板的傳參也支持缺省值:
#include<iostream>
using namespace std;
template<typename T>
void Test(T arg1,T arg2=20) // 把“&”引用去掉
{
T temp = arg1;
arg1 = arg2;
arg2 = temp;
cout << arg1 << " " << arg2 << endl;
}
int main()
{
int a = 10;
Test(a);
}
2.4 模板函數(shù)與普通函數(shù)同時(shí)存在情況
函數(shù)在調(diào)用的時(shí)候,首先會(huì)去調(diào)用已經(jīng)存在的函數(shù)。當(dāng)參數(shù)和已存在的函數(shù)不匹配時(shí),才會(huì)調(diào)用函數(shù)模板
#include<iostream>
using namespace std;
template<typename T>
void Test(T arg1,T arg2 = 90)
{
cout << "Test temp " << arg1 << " " << arg2 << endl;
}
void Test(int arg1,int arg2)
{
cout << "Test " << arg1 << " " << arg2 << endl;
}
int main()
{
int a = 10, b = 20;
double c = 5.2, d = 10.5;
Test(a);
Test(a, b);
Test(a, (int)c); // 強(qiáng)轉(zhuǎn)
Test((double)a, c); // 強(qiáng)轉(zhuǎn)
Test<int>(a, c); // 直接指定為int
}
// 輸出結(jié)果:
Test temp 10 90
Test 10 20
Test 10 5
Test temp 10 5.2
Test temp 10 5
2.5 函數(shù)模板不支持定義和聲明分離
函數(shù)模板的聲明和定義要放在一個(gè)頭文件中。在部分使用場(chǎng)景,會(huì)使用.hpp
來(lái)表示這個(gè)頭文件是包含了函數(shù)定義的(即.h和.cpp
的集合體)。需要注意,這并不是一個(gè)硬性要求,你也可以直接使用.h
,并將聲明和定義放入其中。因?yàn)閱为?dú)的.h
聲明會(huì)在源文件頂部展開(kāi),而此時(shí)函數(shù)模板正常推演參數(shù),但編譯器并沒(méi)有找到函數(shù)的實(shí)現(xiàn),即這是一個(gè)沒(méi)有地址的函數(shù)。從而導(dǎo)致編譯器找不到函數(shù)的地址,產(chǎn)生了符號(hào)表的鏈接錯(cuò)誤。其實(shí)是有的,我們可以在模板函數(shù)定義的.cpp
中對(duì)我們需要使用的函數(shù)進(jìn)行顯式實(shí)例化指定
//頭文件
//聲明
template<typename T>
void Test(T arg1, T arg2);
//源文件
//定義
template<typename T>
void Test(T arg1, T arg2)
{
cout << arg1 << " " << arg2 << endl;
}
//在源文件中顯式實(shí)例化
template
void Test<int>(int arg1, int arg2);
template
void Test<double>(double arg1, double arg2);
顯式實(shí)例化需要對(duì)我們要用的所有函數(shù)進(jìn)行實(shí)例化,比如你需要用double類(lèi)型,只顯示實(shí)例化了int類(lèi)型是不行的,依舊會(huì)報(bào)錯(cuò)。這樣感覺(jué)非常多余……!所以還是把聲明和定義放在同一個(gè)文件里面清晰明了一些。
3 類(lèi)模板
正如我們定義函數(shù)模板一樣,我們也可以定義類(lèi)模板。泛型類(lèi)聲明的一般形式如下所示:
template <class type> class class-name {
.
.
.
}
在這里,type 是占位符類(lèi)型名稱(chēng),可以在類(lèi)被實(shí)例化的時(shí)候進(jìn)行指定。您可以使用一個(gè)逗號(hào)分隔的列表來(lái)定義多個(gè)泛型數(shù)據(jù)類(lèi)型。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-530955.html
下面的實(shí)例定義了類(lèi) Stack<>,并實(shí)現(xiàn)了泛型方法來(lái)對(duì)元素進(jìn)行入棧出棧操作:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-530955.html
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class T>
class Stack {
private:
vector<T> elems; // 元素
public:
void push(T const&); // 入棧
void pop(); // 出棧
T top() const; // 返回棧頂元素
bool empty() const{ // 如果為空則返回真。
return elems.empty();
}
};
template <class T>
void Stack<T>::push (T const& elem)
{
// 追加傳入元素的副本
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// 刪除最后一個(gè)元素
elems.pop_back();
}
template <class T>
T Stack<T>::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// 返回最后一個(gè)元素的副本
return elems.back();
}
int main()
{
try {
Stack<int> intStack; // int 類(lèi)型的棧
Stack<string> stringStack; // string 類(lèi)型的棧
// 操作 int 類(lèi)型的棧
intStack.push(7);
cout << intStack.top() <<endl;
// 操作 string 類(lèi)型的棧
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}
// 輸出結(jié)果:
7
hello
Exception: Stack<>::pop(): empty stack
到了這里,關(guān)于C++中的泛型詳細(xì)講解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!