今日分享:C++模板(全詳解)

😎【博客主頁:你最愛的小傻瓜】😎

🤔【本文內容:C++模板?😍? ? ? 】🤔

---------------------------------------------------------------------------------------------------------------------------------

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;
}

模板

template<typename T>
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}

函數重載,模板。一眼望過去就能看到,模板的代碼量比函數重載代碼量少很多。

那么下面我們要來了解一下模板是怎么制作的。

泛型編程:編寫與類型無關的通用代碼,是代碼復用的一種手段。模板是泛型編程的基礎

模板是分兩類的:1.函數模板? 2.類模板。

1.函數模板:

函數模板格式:
template<typename T1, typename T2,......,typename Tn>
返回值類型 函數名(參數列表){}
typename是用來定義模板參數關鍵字,也可以使用class(切記:不能使用struct代替class)。
template<typename T>
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}

上述代碼:template <typename T>是重中之重。我們要模板的原因就是類型,只要把識別類型的事交給編譯器去做就沒有我們的事了,直接做個甩手掌柜,起飛。

在編譯器編譯階段,對于模板函數的使用,編譯器需要根據傳入的實參類型來推演生成對應類型的函數以供調用。

我們編譯器的工作:

老大,老大!!!既然編譯器能識別類型,那么我就不要他識別,能不能行?哎嘿。

我看你挺刑的。用不同類型的參數使用函數模板時,稱為函數模板的實例化在我們的函數實例化中,是會有:隱式實例化顯式實例化。

1. 隱式實例化:讓編譯器根據實參推演模板參數的實際類型。

2. 顯式實例化:在函數名后的<>中指定模板參數的實際類型。

那么之所以要顯示實例化,我們用代碼來理解

template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);Add(d1, d2);/*該語句不能通過編譯,因為在編譯期間,當編譯器看到該實例化時,需要推演其實參類型通過實參a1將T推演為int,通過實參d1將T推演為double類型,但模板參數列表中只有一個T,編譯器無法確定此處到底該將T確定為int 或者 double類型而報錯注意:在模板中,編譯器一般不會進行類型轉換操作,因為一旦轉化出問題,編譯器就需要背黑鍋Add(a1, d1);*/// 此時有三種處理方式:1. 用戶自己來強制轉化 2. 使用顯式實例化 3.定義多個TAdd(a, (int)d);
return 0;
}
在函數傳參的時候我們會把兩個不同類型的參數傳上去,那么編譯器就會識別到兩個類型,這時編譯器就會迷茫,不知道選那個了。(如果類型不匹配,編譯器會嘗試進行隱式類型轉換,如果無法轉換成功編譯器將會報錯。
處理方式:1. 用戶自己來強制轉化 2. 使用顯式實例化。3.定義多個T
顯式實例化:(在函數名后的<>中指定模板參數的實際類型
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main(void)
{int a = 10;double b = 20.0;// 顯式實例化Add<int>(a, b);return 0;
}
定義多個T:(單個T,傳不同類型的參數編譯器識別不知道用哪個,多個就可以了)

template<class T1, class T2>
T1 Add(T1 left, T2 right)
{return left + right;
}
void Test()
{Add(1, 2); Add(1, 2.0);
}

那是不是我們之后就不用寫類型了,直接用模板了。

額額額 》》》

一個非模板函數可以和一個同名的函數模板同時存在,而且該函數模板還可以被實例化為這個非模板函數。這要看那個更匹配。對于非模板函數和同名函數模板,如果其他條件都相同,在調動時會優先調用非模板函數而不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數, 那么將選擇模板。
// 專門處理int的加法函數
int Add(int left, int right)
{return left + right;
}
// 通用加法函數
template<class T>
T Add(T left, T right)
{return left + right;
}
void Test()
{Add(1, 2); // 與非模板函數匹配,編譯器不需要特化Add<int>(1, 2); // 調用編譯器特化的Add版本
}
. 模板函數不允許自動類型轉換,但普通函數可以進行自動類型轉換,所以說各有優勢。

2.類模板:

類模板定義格式:

template<class T1, class T2, ..., class Tn>
class 類模板名
{// 類內成員定義
};

我們就拿動態順序表:Vector

你可以將函數模板結合來看,因為類的組成里有成員函數,成員變量。

// 動態順序表
// 注意:Vector不是具體的類,是編譯器根據被實例化的類型生成具體類的模具
//這里只是講一下類模板。Vector類一些功能這里沒實現
template<class T>
class Vector
{ 
public :Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}// 使用析構函數演示:在類中聲明,在類外定義。~Vector();void PushBack(const T& data);void PopBack();// ...size_t Size() {return _size;}T& operator[](size_t pos)
{assert(pos < _size);return _pData[pos];}private:T* _pData;size_t _size;size_t _capacity;
};
// 注意:類模板中函數放在類外進行定義時,需要加模板參數列表
template <class T>
Vector<T>::~Vector()
{if(_pData)delete[] _pData;_size = _capacity = 0;
}

1. 模板就是因類型不同而出的,所以與函數模板一樣,類模板,將類型換成T就行。

2. 函數在外類定義時,要加模板參數列表。

類的實例化:

類模板實例化與函數模板實例化不同,類模板實例化需要在類模板名字后跟<>,然后將實例化的類型放在<> 中即可,類模板名字不是真正的類,而實例化的結果才是真正的類。
// Vector類模板名,Vector<int>才是類
Vector<int> s1;
Vector<double> s2;

玩轉模板:晉升大大怪將軍。

1. 非類型模板參數

在前面已經了解了模板,但有些細節沒有,接下來就是玩轉模板時刻。

非類型模板參數就是相當于C語言里的define宏替換。就是用一個常量作為類(函數)模板的一個參數,在類(函數)模板中可將該參數當成常量來使用

1. 浮點數、類對象以及字符串是不允許作為非類型模板參數的
2. 非類型的模板參數必須在編譯期就能確認結果
namespace xin
{// 定義一個模板類型的靜態數組template<class T, size_t N = 10>class array{public:T& operator[](size_t index){return _array[index];}const T& operator[](size_t index)const{return _array[index];}size_t size()const{return _size;}bool empty()const{return 0 == _size;}private:T _array[N];size_t _size;};
}

2. 模板的特化

template<class T>
bool Less(T left, T right)
{return left < right;
}
int main()
{Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl;Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 可以比較,結果錯誤return 0;
}

當我們用一個日期類對象的指針進行比較時,會出現錯誤。通常指針地址的大小是隨機的。

這就是我們要模板特化(寫一個特殊情況的)的原因。

這種情況有很多:1.?數組類型2.?函數指針類型3.?引用類型4.?智能指針5.?容器類型6.?基本類型與自定義類型的區分。

簡單的了解了一下模板特化,接下來是怎么實現。

模板特化中分為函數模板特化與類模板特化。

函數模板特化:

函數模板的特化步驟:
1. 必須要先有一個基礎的函數模板
2. 關鍵字template后面接一對空的尖括號<>
3. 函數名后跟一對尖括號,尖括號中指定需要特化的類型

4. 函數形參表:?必須要和模板函數的基礎參數類型完全相同,如果不同編譯器可能會報一些奇怪的錯誤。

// 函數模板 -- 參數匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}
// 對Less函數模板進行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}
int main()
{cout << Less(1, 2) << endl;Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl;Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 調用特化之后的版本,而不走模板生成了return 0;
}
一般情況下如果函數模板遇到不能處理或者處理有誤的類型,為了實現簡單通常都是將該函數直接給出。(也就是說不給模板特化,沒性價比)
bool Less(Date* left, Date* right)
{return *left < *right;
}

類模板特化:

分為 1.全特化 2.?偏特化

1.全特化

全特化即是將模板參數列表中所有的參數都確定化
template<class T1, class T2>
class Data
{
public:Data() {cout<<"Data<T1, T2>" <<endl;}
private:T1 _d1;T2 _d2;
};
//全特化
template<>
class Data<int, char>
{
public:Data() {cout<<"Data<int, char>" <<endl;}
private:int _d1;char _d2;
};
void TestVector()
{Data<int, int> d1;Data<int, char> d2;
}

格式:1.template<>? 2.類的后面加<? x,x? >里面的x是要用的類型。里面的類型就不是T了,而是知道的。

2.?偏特化

偏特化:任何針對模版參數進一步進行條件限制設計的特化版本

1.部分特化

將模板參數類表中的一部分參數特化
// 將第二個參數特化為int
template <class T1>
class Data<T1, int>
{
public:Data() {cout<<"Data<T1, int>" <<endl;}
private:T1 _d1;int _d2;
};

2.參數更進一步的限制

偏特化并不僅僅是指特化部分參數,而是針對模板參數更進一步的條件限制所設計出來的一個特化版 本。
//兩個參數偏特化為指針類型
template <typename T1, typename T2>
class Data <T1*, T2*>
{ 
public:Data() {cout<<"Data<T1*, T2*>" <<endl;}private:
T1 _d1;T2 _d2;
};
//兩個參數偏特化為引用類型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout<<"Data<T1&, T2&>" <<endl;}private:const T1 & _d1;const T2 & _d2; };
void test2 () 
{Data<double , int> d1; // 調用特化的int版本Data<int , double> d2; // 調用基礎的模板 Data<int *, int*> d3; // 調用特化的指針版本Data<int&, int&> d4(1, 2); // 調用特化的指針版本
}

其實所謂的特化就是將一些模板實現的函數或類無法應對特殊類型,因此要寫一個適合這個特殊類型的函數或類模板。

3.模板分離編譯

一個程序(項目)由若干個源文件共同實現,而每個源文件單獨編譯生成目標文件,最后將所有目標文件鏈 接起來形成單一的可執行文件的過程稱為分離編譯模式。
模板的聲明與定義分離開,在頭文件中進行聲明,源文件中完成定義:
// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
// main.cpp
#include"a.h"
int main()
{Add(1, 2);Add(1.0, 2.0);return 0;
}

為了解決這種編譯器沒看到模板函數的實力化(模板的 “按需生成” 機制與分離編譯的 “獨立編譯 + 鏈接” 模型不兼容):

1. 將聲明和定義放到一個文件 "xxx.cpp" 里面或者xxx.h其實也是可以的。推薦使用這種。
2. 模板定義的位置顯式實例化。這種方法不實用,不推薦使用。

??總結

相信堅持下來的你一定有了滿滿的收獲。那么也請老鐵們多多支持一下,為愛博,點點舉報,偶布,是點點關注,收藏,點贊。??

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95605.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95605.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95605.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ramdisk內存虛擬盤(一)——前世今生

1990 年代&#xff1a;前因——“硬盤太慢、驅動太多” 背景&#xff1a;早期 Linux 根文件系統要么在軟盤、要么在 IDE 硬盤&#xff0c;內核把對應的軟盤/IDE 驅動編進去即可順利掛載。矛盾出現&#xff1a;隨著 SCSI、PCMCIA、USB、RAID 控制器等百花齊放&#xff0c;如果把…

ETH持續上漲推動DEX熱潮,交易活躍度飆升的XBIT表現強勢出圈

BOSS Wallet 8月15日訊&#xff0c;隨著ETH價格在過去24小時內強勢拉升至4300美元&#xff0c;整個加密市場再度掀起漲勢狂潮&#xff0c;鏈上交易活躍度空前高漲。其中&#xff0c;去中心化交易所平臺迅速成為市場焦點&#xff0c;其平臺活躍度與交易量雙雙上漲&#xff0c;吸…

Stand-In - 輕量級人物一致性視頻生成 高保真視頻人臉交換 ComfyUI工作流 支持50系顯卡 一鍵整合包下載

Stand-In 是一個輕量級、即插即用的身份保護視頻生成框架&#xff0c;只需要上傳一張人物照片&#xff0c;加上一段提示詞&#xff0c;即可生成高度一致性的高保真人物視頻&#xff0c;人臉相似度和自然都幾乎達到100%還原水平。 Stand-In 能把任何一張人臉&#xff08;甚至動漫…

vue3相關基礎

1、ref和reactive的區別兩者都是響應式數據的聲明。Reactive只適用于非基本數據類型&#xff0c;如對象&#xff0c;數組等。而ref是兼容適用于reactive的的數據類型的以及其他數據&#xff0c;靈活性較高。ref聲明的變量取值時需要.value。在<template></template>…

云手機存儲和本地存儲的區別

云手機存儲通常指云存儲&#xff0c;即數據存儲在云端服務器&#xff0c;本地存儲則是將數據存儲在用戶設備硬件中&#xff0c;主要區別體現在存儲位置、訪問方式、依賴條件等多個方面&#xff0c;具體如下&#xff1a;本地存儲主要是將數據存儲在用戶自有設備的物理硬件中&…

【科研繪圖系列】R語言繪制三維曲線圖

文章目錄 介紹 加載R包 數據下載 導入數據 數據預處理 畫圖 系統信息 參考 介紹 【科研繪圖系列】R語言繪制三維曲線圖 加載R包 library(tidyverse) library(ggsignif) library(RColorBrewer) library(dplyr) library(reshape2) library(grid

python常用包

以下按類別列舉10個常用Python包&#xff0c;并以一句話概括其核心作用&#xff1a; 一、數據分析與科學計算 NumPy&#xff1a;提供高性能多維數組及數學運算&#xff0c;是數值計算的基礎庫。Pandas&#xff1a;通過DataFrame結構實現高效表格數據清洗、分析與處理。SciPy&am…

“ 船新版本 ”

在 GeeLark 最新版本中&#xff0c;增強了 AIGC 生成能力以及 AI 協助自定義任務開發功能&#xff0c;給用戶優化構建從內容生產到運營自動化的完整技術鏈&#xff0c;為跨境電商及企業用戶提供更完善的智能化解決方案&#xff0c;效率翻倍輕松出海。 AIGC 接入 MiniMax-Hailuo…

力扣 —— 二分查找

搜索插入位置 35. 搜索插入位置 - 力扣&#xff08;LeetCode&#xff09; 算法思想&#xff1a; class Solution(object):def searchInsert(self, nums, target):left0 rightlen(nums)-1while left < right :mid (left right) // 2if nums[mid] < target:left mid 1…

USB ADB 簡介

概念 ADB 是 Android 平臺的 調試橋接協議&#xff0c;允許主機&#xff08;PC&#xff09;與 Android 設備通信。 通過 ADB&#xff0c;開發者可以執行命令、調試應用、傳輸文件、訪問 shell、調試 logcat 等。 ADB 運行在 USB 或 TCP/IP 上&#xff0c;但最常用的是 USB 連…

【Golang】:數據類型

目錄 1. 基本數據類型 1.1 布爾類型 1.2 整數類型 1.3 浮點數類型 1.4 復數類型 1.5 字符類型 1.6 字符串類型 2. 類型轉換 2.1 基本數據類型 → string 2.2 string → 基本數據類型 3. 常量 1. 基本數據類型 1.1 布爾類型 Go中的布爾類型取值為true或false&#…

旋鈕鍵盤項目---foc講解(開環)

這里就不過多的講解什么原理&#xff0c;公式的變換了&#xff0c;感興趣的可以看燈哥開源&#xff0c;講解的非常好的。當然&#xff0c;更細致的講解&#xff0c;也可以看b站其他教學。 我這里主要講解我對于開環部分的理解&#xff0c;以及stm32代碼的實現邏輯。可以看作是…

數據科學與計算:爬蟲和數據分析案例筆記

案例 1&#xff1a;中國大學排名爬取與分析 一、任務描述 目標&#xff1a;爬取高三網中國大學排名一覽表&#xff0c;提取學校名稱、總分、全國排名、星級排名、辦學層級等數據&#xff0c;并保存為 CSV 文件。 網址&#xff1a;2021中國的大學排名一覽表_高三網 二、任務…

華測科技的3D GPR數據分析

很高興得到了張總的支持&#xff0c;獲得了他們雷達的數據&#xff0c;并寫了雷達數據讀取和轉換文件。1 背景搜索后發現華測實力很強&#xff0c;因為他們可達到100km/h的時速&#xff0c;以前我只知道中電眾益可以達到這個速度。2數據格式分析2.1 華測數據因為長時間不編程&a…

最長鏈(二叉樹直徑DFS)

題目描述現給出一棵N個結點二叉樹&#xff0c;問這棵二叉樹中最長鏈的長度為多少&#xff0c;保證了1號結點為二叉樹的根。輸入第1行為包含了一個正整數N&#xff0c;為這棵二叉樹的結點數&#xff0c;結點標號由1至N。 接下來N行&#xff0c;這N行中的第i行包含兩個正整數l[i]…

802.11 Wi-Fi 競爭機制深度分析:CSMA/CA 與 DCF

802.11 Wi-Fi 競爭機制深度分析&#xff1a;CSMA/CA 與 DCF 一、核心機制&#xff1a;CSMA/CA&#xff08;載波偵聽多路訪問/沖突避免&#xff09; 傳統以太網使用 CSMA/CD&#xff08;沖突檢測&#xff09;&#xff0c;但無線環境中無法實現沖突檢測&#xff0c;因此802.11采用…

【Go語言-Day 36】構建專業命令行工具:`flag` 包入門與實戰

Langchain系列文章目錄 01-玩轉LangChain&#xff1a;從模型調用到Prompt模板與輸出解析的完整指南 02-玩轉 LangChain Memory 模塊&#xff1a;四種記憶類型詳解及應用場景全覆蓋 03-全面掌握 LangChain&#xff1a;從核心鏈條構建到動態任務分配的實戰指南 04-玩轉 LangChai…

C語言——深入理解指針(四)

C語言——深入理解指針&#xff08;四&#xff09; 數組名的意義sizeof&#xff08;數組名&#xff09;&#xff0c;且數組名單獨放在sizeof內部&#xff0c;則這里的數組名表示整個數組&#xff0c;計算的是整個數組的大小&數組名&#xff0c;這里的數組名表示的是整個數組…

LeetCode 刷題【42. 接雨水】

42. 接雨水 自己做 解&#xff1a;雙指針左右分割容器 class Solution { public:int trap(vector<int>& height) {int res 0;int len height.size();if(len < 2) //構不成一個容器了&#xff0c;直接返回return res;int end len - 1; //右邊界int…

網絡的基本概念、通信原理以及網絡安全問題

目錄 1、 什么是網絡&#xff1f; &#xff08;1&#xff09;網絡的概念與本質 &#xff08;2&#xff09;電壓信號的合并與抵消 &#xff08;3&#xff09;電壓的本質 2、中轉設備 &#xff08;1&#xff09;背景 &#xff08;2&#xff09;中轉設備的處理能力與編程能…