柚子快報邀請碼778899分享:C++——模板初階
目錄
引言
泛型編程
函數(shù)模板
1.函數(shù)模板的基本用法
2.函數(shù)模板的原理
3.函數(shù)模板的實例化
3.1 隱式實例化
3.2 顯式實例化
(1)先強制類型轉(zhuǎn)換
(2)指定模板參數(shù)的實際類型
4.函數(shù)模板的匹配規(guī)則
類模板
1.類模板的基本用法
2.棧類模板的實現(xiàn)
3.類模板的實例化
4.類模板為何優(yōu)于 typedef
非類型模板參數(shù)
1.概念與用法
2.使用示例
模板的特化
1.函數(shù)模板特化
2.類模板的特化
3.模板的特化方式
3.1 全特化
3.2 偏特化
結(jié)束語
引言
在學(xué)習(xí)完類與對象之后,我們接下來學(xué)習(xí)C++中一重要特性——模板
泛型編程
首先我們先來思考:如何實現(xiàn)一個通用的交換函數(shù)呢?
void swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}
顯然,這個代碼有很大的弊端,每一種類型都寫一個,同時每個函數(shù)名都不能相同,無疑是非常繁瑣的。這一點我們可以利用C++中函數(shù)重載實現(xiàn),支持函數(shù)可以同名。
像這樣:
void swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}
void swap(double& x, double& y)
{
int tmp = x;
x = y;
y = tmp;
}
但是我們?nèi)绻枰獙崿F(xiàn)支持字符串,或者其他自定義類型,還需要繼續(xù)添加一個swap函數(shù)。這樣實在是太麻煩了,也顯得代碼十分冗長。
那能否告訴編譯器一個模子,讓編譯器根據(jù)不同的類型利用該模子來生成代碼呢?
為了解決這個問題,C++引入了模版這個概念,并且依次延伸出了泛型編程的思想。
泛型編程:編寫與類型無關(guān)的通用代碼,是代碼復(fù)用的一種手段,模板是泛型編程的基礎(chǔ)。
C++中的模板主要分為兩種:函數(shù)模板和類模板。
函數(shù)模板
1.函數(shù)模板的基本用法
函數(shù)模板允許定義一個函數(shù),其參數(shù)類型或返回類型在編譯時才確定。這樣,同一個函數(shù)模板可以用于多種不同的數(shù)據(jù)類型,而無需為每種類型都編寫一個獨立的函數(shù)。
具體語法如下:
template
void func(const T& x)
{
// 函數(shù)體
}
也可以實現(xiàn)兩個類型及以上的模板:
template
void func(const T1& x, const T2& y)
{
// 函數(shù)體
}
我們可以根據(jù)函數(shù)模板寫一段實現(xiàn)針對不同類型的交換函數(shù),代碼如下:
template
void Swap(T& x, T& y)
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0;
int b = 1;
cout << "交換前:" << a << "," << b << endl;
Swap(a, b);
cout << "交換后:" << a << "," << b << endl;
double c = 1.14, d = 5.14;
cout << "交換前:" << c << "," << d << endl;
Swap(c, d);//調(diào)用浮點型
cout << "交換后:" << c << "," << d << endl;
return 0;
}
注意:typename是用來定義模板參數(shù)關(guān)鍵字,也可以使用class(切記:不能使用 struct 代替 class)
輸出結(jié)果為:
2.函數(shù)模板的原理
我們思考一下:不同的類型調(diào)用的函數(shù)模版是否是同一個函數(shù)?
我們接下來通過反匯編來觀察一下上面那段代碼:
通過反匯編可以觀察到:調(diào)用實參類型不同時,調(diào)用的函數(shù)不同。
那么函數(shù)模版到底是如何調(diào)用的呢?
實際上,函數(shù)模板是一個藍圖,它本身并不是函數(shù),是編譯器用使用方式產(chǎn)生特定具體類型函數(shù)的模具。 所以其實模板就是將本來應(yīng)該我們做的重復(fù)的事情交給了編譯器
在編譯器編譯階段,對于模板函數(shù)的使用,編譯器需要根據(jù)傳入的實參類型來推演生成對應(yīng) 類型的函數(shù)以供調(diào)用。比如:當用double類型使用函數(shù)模板時,編譯器通過對實參類型的推演,將T確定為double類型,然后產(chǎn)生一份專門處理double類型的代碼,對于字符類型也是如此。
3.函數(shù)模板的實例化
用不同類型的參數(shù)使用函數(shù)模板時,稱為函數(shù)模板的實例化。模板參數(shù)實例化分為:隱式實例化 和顯式實例化。
3.1 隱式實例化
隱式實例化:讓編譯器根據(jù)實參推演模板參數(shù)的實際類型
template
T Add(const T& x, const T& y)
{
return x + y;
}
int main()
{
// 編譯器自動推導(dǎo)類型
cout << (Add(3, 5)) << endl;
return 0;
}
3.2 顯式實例化
在某些特定的場景無法使用隱式實例化,如下所示:
template
T Add(const T& x, const T& y)
{
return x + y;
}
int main()
{
int a = 1;
double b = 3.14;
cout << (Add(a, b)) << endl;
return 0;
}
在這里編譯器會報錯:
這是因為編譯器不知道把T推演成 int 還是 double 。為了解決這個問題,我們通常有以下兩種方法:
(1)先強制類型轉(zhuǎn)換
template
T Add(const T& x, const T& y)
{
return x + y;
}
int main()
{
int a = 1;
double b = 3.14;
cout << (Add(a, (int)b)) << endl;
return 0;
}
(2)指定模板參數(shù)的實際類型
template
T Add(const T& x, const T& y)
{
return x + y;
}
int main()
{
int a = 1;
double b = 3.14;
cout << (Add
return 0;
}
如果類型不匹配,編譯器會嘗試進行隱式類型轉(zhuǎn)換,如果無法轉(zhuǎn)換成功編譯器將會報錯。
4.函數(shù)模板的匹配規(guī)則
(1)一個非模板函數(shù)可以和一個同名的函數(shù)模板同時存在,而且該函數(shù)模板還可以被實例化為這個非模板函數(shù)
// 專門處理int的加法函數(shù)
int Add(int x, int y)
{
cout << "int Add(int x, int y)" << endl;
return x + y;
}
// 通用加法函數(shù)
template
T Add(T x, T y)
{
cout << "T Add(T x, T y)" << endl;
return x + y;
}
void Test()
{
Add(1, 2); // 與非模板函數(shù)匹配,編譯器不需要特化
Add
}
int main()
{
Test();
return 0;
}
輸出結(jié)果為:
(2)對于非模板函數(shù)和同名函數(shù)模板,如果其他條件都相同,在調(diào)動時會優(yōu)先調(diào)用非模板函數(shù)而 不會從該模板產(chǎn)生出一個實例。如果模板可以產(chǎn)生一個具有更好匹配的函數(shù), 那么將選擇模板
// 專門處理int的加法函數(shù)
int Add(int x, int y)
{
cout << "int Add(int left, int right)" << endl;
return x + y;
}
// 通用加法函數(shù)
template
T1 Add(T1 x, T2 y)
{
cout << "T1 Add(T1 x, T2 y)" << endl;
return x + y;
}
void Test()
{
Add(1, 2); // 與非函數(shù)模板類型完全匹配,不需要函數(shù)模板實例化
Add(1, 2.0); // 模板函數(shù)可以生成更加匹配的版本,編譯器根據(jù)實參生成更加匹配的Add函
}
int main()
{
Test();
return 0;
}
輸出結(jié)果為:
類模板
類模版與函數(shù)模版類似,只不過作用對象是類。
1.類模板的基本用法
template
class 類模板名
{
? // 類內(nèi)成員定義
}
來看個簡單的例子:
template
class A
{
public:
A(T x, T y)
:_x(x)
,_y(y)
{
}
T add()
{
return _x + _y;
}
private:
T _x;
T _y;
};
int main()
{
A
cout << a.add() << endl;
A
cout << b.add() << endl;
return 0;
}
注意:類模板中函數(shù)放在類外進行定義時,需要加模板參數(shù)列表。
如下所示:
template
class A
{
public:
A(T x, T y)
:_x(x)
,_y(y)
{
}
T add();
private:
T _x;
T _y;
};
//類外實現(xiàn)
template
T A
{
return _x + _y;
}
輸出結(jié)果為:
2.棧類模板的實現(xiàn)
我們可以嘗試寫一個棧類的模板:
template
class Stack
{
public:
// 構(gòu)造函數(shù)
Stack(int n = 4)
: _array(new T[n])
, _capacity(n)
, _size(0)
{
// ...
}
// 析構(gòu)函數(shù)
~Stack()
{
delete[] _array;
_array = nullptr;
_capacity = _size = 0;
}
Stack(const Stack&) = delete;
Stack& operator=(const Stack&) = delete;
// 向棧中添加新元素x
void Push(const T& x)
{
// 檢查是否需要擴容
if (_size == _capacity)
{
T* tmp = new T[2 * _capacity];
memcpy(tmp, _array, sizeof(T) * _size);
delete[] _array;
_array = tmp;
_capacity *= 2;
}
_array[_size++] = x;
}
// 彈出棧頂元素
void Pop()
{
assert(!IsEmpty()); // 確保棧不為空
--_size;
}
// 獲取棧頂元素(不彈出)
T& Top()
{
assert(!IsEmpty()); // 確保棧不為空
return _array[_size - 1];
}
// 檢查棧是否為空
bool IsEmpty() const
{
return _size == 0;
}
// 獲取棧的大小
size_t Size() const
{
return _size;
}
private:
T* _array;
size_t _capacity;
size_t _size;
};
順便寫個main函數(shù)測試一下:
int main()
{
Stack
intStack.Push(1);
intStack.Push(2);
intStack.Push(3);
intStack.Push(4);
cout << "棧頂元素: " << intStack.Top() << endl;
intStack.Pop(); // 彈出一個元素
cout << "現(xiàn)在棧頂元素: " << intStack.Top() << endl;
cout << "棧的大小: " << intStack.Size() << endl;
// 測試IsEmpty(此時棧不應(yīng)為空)
cout << "棧是否為空: " << (intStack.IsEmpty() ? "是" : "否") << endl;
return 0;
}
輸出結(jié)果為:
3.類模板的實例化
類模板實例化與函數(shù)模板實例化不同,類模板實例化需要在類模板名字后跟<>,然后將實例化的 類型放在<>中即可,類模板名字不是真正的類,而實例化的結(jié)果才是真正的類。所以利用類模版創(chuàng)建對象時必須要對類模版先顯式實例化。
像這個:
int main()
{
Stack
st1.Push(1);
Stack
st2.Push(3.14);
return 0;
}
4.類模板為何優(yōu)于 typedef
類模板相比于typedef在C++編程中具有顯著的優(yōu)越性,這主要體現(xiàn)在它們各自的功能和應(yīng)用場景上。typedef主要用于為現(xiàn)有的類型定義一個新的名稱(別名),而類模板則提供了一種更靈活、更強大的方式來定義可以在多種數(shù)據(jù)類型上工作的類。
類模板:通過類模板,可以定義一個與具體數(shù)據(jù)類型無關(guān)的類,這個類可以在實例化時指定數(shù)據(jù)類型。這種特性使得類模板能夠在多種數(shù)據(jù)類型上重用相同的代碼,減少了代碼冗余,提高了代碼的復(fù)用性。同時,它也支持泛型編程,使得函數(shù)或類可以更加通用,不依賴于具體的數(shù)據(jù)類型。
typedef:typedef只是為現(xiàn)有的類型定義了一個新的名稱,它本身并不支持泛型編程,也不具備在不同數(shù)據(jù)類型上重用代碼的能力。
非類型模板參數(shù)
非類型模板參數(shù)(Non-type Template Parameters)是C++模板編程中的一個重要概念,它允許模板參數(shù)不僅僅是類型,還可以是常量值或表達式。
1.概念與用法
概念:非類型模板參數(shù)是指模板參數(shù)列表中不是類型參數(shù)的部分,它們可以是整型常量、枚舉常量、指針或引用等,但不能是浮點數(shù)或類類型的對象。這些參數(shù)在模板實例化時被具體的值所替代。
用法:非類型模板參數(shù)常用于需要根據(jù)不同值生成不同代碼的場景,如定義固定大小的數(shù)組、控制循環(huán)次數(shù)等
2.使用示例
// 定義一個模板類型的靜態(tài)數(shù)組
template
class array
{
public:
T& operator[](size_t index)
{
return _array[index];
}
const T& operator[](size_t index)const
{
return _array[index];
}
private:
T _array[n];//定義數(shù)組
};
我們可以通過傳參實現(xiàn)控制靜態(tài)數(shù)組的大小,如下所示:
array
array
非類型模板參數(shù)必須是常量表達式,且其值在編譯時已知。
浮點類型和非類類型對象不能作為非類型模板參數(shù)。
模板的特化
模板的特化,在C++編程中,是一種對模板的特定類型或類型組合提供專門實現(xiàn)的技術(shù)。模板特化有時也被稱為模板的具體化,主要分為函數(shù)模板特化和類模板特化兩種。
1.函數(shù)模板特化
函數(shù)模板特化是在一個統(tǒng)一的函數(shù)模板不能在所有類型實例下正常工作時,需要定義類型參數(shù)在實例化為特定類型時函數(shù)模板的特定實現(xiàn)版本。這通常是因為對于某些特定類型,通用的函數(shù)模板實現(xiàn)可能無法滿足需求或者會產(chǎn)生不正確的結(jié)果。
函數(shù)模板特化需要做到:
(1)存在基礎(chǔ)模板
(2)有關(guān)鍵字template與空尖括號
(3)指定特化類型
(4)保持參數(shù)一致性
// 模板類A,包含一個公共的setValue函數(shù)
template
class A
{
public:
T value;
A(T v) : value(v)
{
// ...
}
// 一個通用的setValue函數(shù)(不是特化的主體)
void setValue(T v)
{
value = v;
cout << "泛化版本的setValue" << endl;
}
};
template
void Print(T v)
{
cout << "泛化版本的Print: " << v << endl;
}
// 對Print函數(shù)的int類型特化
template<>
void Print
{
cout << "特化版本的Print: " << v << endl;
}
int main()
{
A
aDouble.setValue(2.71); // 調(diào)用泛化版本的setValue
Print(3.14); // 調(diào)用泛化版本的Print
Print(42); // 調(diào)用特化版本的Print
return 0;
}
?輸出結(jié)果為:
2.類模板的特化
類模版的特化與函數(shù)模版特化類似。
舉個簡單的例子:
template
class A
{
public:
// 泛化版本的構(gòu)造函數(shù),接受一個類型為T的參數(shù)v
// 用于初始化成員變量_value
A(T v) : _value(v)
{
cout << "泛化版本" << endl;
}
private:
T _value;
};
template<>
class A
{
public:
// 特化版本的構(gòu)造函數(shù),接受一個int類型的參數(shù)v
// 用于初始化成員變量_value
A(int v) : _value(v)
{
cout << "特化版本" << endl;
}
private:
int _value;
};
int main()
{
A
A
return 0;
}
輸出結(jié)果為:
3.模板的特化方式
模板的特化方式主要分為兩種:全特化和偏特化。這些特化方式適用于模板類和模板函數(shù)。
3.1 全特化
全特化是指為模板的所有類型參數(shù)提供一個完全不同于通用模板版本的特殊實現(xiàn)。在全特化中,模板的所有類型參數(shù)都被明確指定,并且為這些特定的類型參數(shù)組合提供了一個全新的實現(xiàn)。
// 泛化版本的類模板A
template
class A
{
public:
A(T1 v1, T2 v2) : _value1(v1), _value2(v2)
{
cout << "泛化版本" << endl;
}
private:
T1 _value1;
T2 _value2;
};
// A的全特化版本,其中T1和T2都被特化為int
template<>
class A
{
public:
A(int v1, int v2) : _value1(v1), _value2(v2)
{
cout << "全特化版本" << endl;
}
private:
int _value1;
int _value2;
};
int main()
{
A
A
return 0;
}
3.2 偏特化
偏特化是指當模板有多個類型參數(shù)時,只對其中一部分類型參數(shù)進行特化,而保留其他類型參數(shù)為通用模板中的形式。偏特化僅適用于類模板,不適用于函數(shù)模板。
// 泛化版本的類模板A
template
class A
{
public:
// 泛化版本的構(gòu)造函數(shù)
A(T1 v1, T2 v2) : _value1(v1), _value2(v2)
{
cout << "泛化版本" << endl;
}
private:
T1 _value1;
T2 _value2;
};
// A的偏特化版本,其中T1被特化為int
template
class A
{
public:
// 偏特化版本的構(gòu)造函數(shù)
A(int v1, T2 v2) : _value1(v1), _value2(v2)
{
cout << "偏特化版本" << endl;
}
private:
int _value1;
T2 _value2;
};
int main()
{
A
A
return 0;
}
結(jié)束語
哇最近有點擺爛,寫的有一捏捏慢。
花了一段時間終于是把模板的一些內(nèi)容簡要的寫了一下。
感謝各位大佬的支持?。?!
求點贊收藏評論關(guān)注?。?!
柚子快報邀請碼778899分享:C++——模板初階
參考文章
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。