C++初階-模板初階

目錄

?

1.泛型編程

2.函數模板

2.1函數模板概念

2.2實現函數模板

2.3模板的原理

2.4函數模板的實例化

2.4.1隱式實例化

2.4.2顯式初始化

2.5模板參數的匹配原則

3.類模板

3.1類模板定義格式

3.2類模板的實例化

4.總結



1.泛型編程

對廣泛的類型法寫代碼,我們之前寫的是一個專用的類型,如果想要用其他類型就要再寫一個專用的函數寫這個功能和實現方式與其他的差不多的代碼,非常麻煩,如:

//交換兩個整型
void Swap(int& a, int& b)
{int c = a;a = b;b = c;
}
//交換兩個浮點型
void Swap(double& a, double& b)
{double c = a;a = b;b = c;
}

我們可以發現,如果我們把第一個函數的int 改為double就可以直接變為第二個函數了,這樣寫起來很費時間,而且如果我們這個函數只用幾次的話,寫了這個是完全沒意義的,所以這有兩個不好的地方:

(1)重載的函數僅僅是類型不同,代碼復用率比較低,只要有新類型出現時,就需要用戶自己增加對應的函數;

(2)代碼的可維護性比較低,一個出錯可能所有的重載均出錯。

我們能否告訴編譯器一個模子,讓編譯器根據不同的類型利用該模子來生成代碼呢?

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

而這就需要我們來通過模板這個東西來實現,模板分為函數模板類模板

2.函數模板

2.1函數模板概念

函數模板代表了一個函數家族,該函數模板與類型無關,根據實參類型產生函數的特定類型版本。

2.2實現函數模板

模板的格式:template<typename T1,typename T2,……,typedname Tn>

其中T1、T2、……T3都是模板的參數,而這個template是關鍵字,typename是用來定義模板參數關鍵字,也可以使用class(切記不可用struct代替)。(到模板進階(C++初階最后的部分)會講class與typename的區別)

如,我們之前實現的交換函數可以這樣寫:

#include<iostream>
using namespace std;
//需要幾個類型就寫幾個參數
template<typename T>
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}
int main()
{//交換兩個整型int a, b;cin >> a >> b;Swap(a, b);cout << a << " " << b << endl;//交換兩個浮點型double c, d;cin >> c >> d;Swap(c, d);cout << c << " " << d << endl;//交換兩個字符型char e, f;cin >> e >> f;Swap(e, f);cout << e << " " << f << endl;return 0;
}

運行結果如下:

如果我們實參不是一個類型,則會報錯:

我們相較于之前寫交換函數只是在函數定義前加了一行模板的定義而已,并且把類型換為模板了而已。

2.3模板的原理

函數模板是一個藍圖,它本身并不是函數,是編譯器用使用方式產生特定具體類型函數的模具。
所以其實模板就是將本來應該我們做的重復的事情交給了編譯器。只是看實參類型用模板去匹配而已,實際上生成的兩個函數都不是一樣的,編譯器根據實參類型生成一個新函數,如果有不同的類型就再生成一個新函數,地址不同。如,我們之前實現的交換函數:

在編譯器編譯階段,對于模板函數的使用,編譯器需要根據傳入的實參類型來推演生成對應
類型的函數以供調用。比如:當用double類型使用函數模板時,編譯器通過對實參類型的推演,
將T確定為double類型,然后產生一份專門處理double類型的代碼,對于字符類型也是如此。

2.4函數模板的實例化

用不同類型的參數使用函數模板時,稱為函數模板的實例化。模板參數實例化分為:隱式實例化
和顯式實例化。

2.4.1隱式實例化

讓編譯器根據實參推演模板參數的實際類型。

和我之前實現的那個交換函數調用時一樣的形式,int a,b;Swap(a,b);則通過自動識別出a和b都是int類型,這就是隱式實例化。

但是如果我們給的是不同的類型,通過識別出兩個實參類型不同就會報錯,這個時候我們要么就改實參為同一類型,要么就用顯式實例化,要么就要一個類型強制類型轉換為另一類型:

#include<iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a = 3, b = 2;double c = 3.024, d = 4.501;cout << Add(a, b) << endl;cout << Add(c, d) << endl;cout << Add(a, (int)c) << endl;cout << Add((double)a, c) << endl;return 0;
}

運行結果如下:

建議不要去運行之前的交換排序代碼,因為它我測試過不僅顯式實例化沒有用處,強制類型轉換也沒有用處,至于原因感興趣的可以去探討一下。

當然這里涉及到精度丟失的問題,所以建議Add這類的函數還是加一個參數寫成如下形式:

但是返回值你也要改一下,要么返回double類型要么返回int類型!

template<class T1,class T2>
T1 Add(const T1& left, const T2& right)
{return left + right;
}

這是一個演示的,如果之后學的更多了,也可能會有其他類型的返回值等等。

2.4.2顯式初始化

我們之前的代碼也可以改為如下形式,這樣的形式也不用強制類型轉換了,直接用<>里面的類型進行生成函數的操作。

#include<iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a = 2, b = 3;double c = 4.53, d = 2.54;//指定參數為int類型(形參)cout << Add<int>(a, c) << endl;//指定參數為double類型(形參)cout << Add<double>(b, d) << endl;return 0;
}

運行結果如下:

當然,運行時輸出的會有一個提示:

也就是說double轉換為int會有小數點后的數據丟失,所以建議把int轉換為double類型!因為這樣算出來的結果才更加準確。

當然不是所有的都可以用兩種實例化的方式:

template<class T>
T* func(size_t n)
{return new T[n];
}

因為形參沒有任何的能代表T的類型,也就是說不能通過類型推理來知道T的類型,所以就只能進行顯式實例化了。

2.5模板參數的匹配原則

(1)一個非模板函數可以和一個同名的函數模板同時存在,而且該函數模板還可以被實例化為這
個非模板函數。

如:

// 專門處理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版本
}

若有int類型的,編譯器會先用現成的(非模板),否則用模板的。但若我們不想用現成的我們就可以直接顯式實例化。

(2)對于非模板函數和同名函數模板,如果其他條件都相同,在調動時會優先調用非模板函數而
不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數, 那么將選擇模板。

如:

#include<iostream>
using namespace std;
// 專門處理int的加法函數
int Add(int left, int right)
{return left + right;
}// 通用加法函數
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{return left + right;
}void Test()
{Add(1, 2);// 與非函數模板類型完全匹配,不需要函數模板實例化Add(1, 2.0);// 模板函數可以生成更加匹配的版本,編譯器根據實參生成更加匹配的
}

只要非模板函數的形參類型與實參全部匹配就用非模板函數,但是如果是兩個參數類型不一樣的情況,發現模板函數用的不會使精度丟失,所以直接用了模板函數了。

(3)?模板函數不允許自動類型轉換,但普通函數可以進行自動類型轉換。

3.類模板

3.1類模板定義格式

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

和之前的方式一樣,當然,不是因為它是類就只能用class來定義參數,也可以用typename定義參數,只是現在沒學到后面,不知道二者的區別而已。

我們可以把之前實現棧來改為這個方式:

#include<iostream>
using namespace std;
// 類模版
template<typename T>
class Stack
{
public:Stack(size_t capacity = 4){_array = new T[capacity];_capacity = capacity;_size = 0;}void Push(const T& data);
private:T* _array;size_t _capacity;size_t _size;
};
// 模版不建議聲明和定義分離到兩個文件.h 和.cpp會出現鏈接錯誤,具體原因后面會講
template<class T>
void Stack<T>::Push(const T& data)
{// 擴容_array[_size] = data;++_size;
}

我們定義這個棧的時候如果成員函數聲明和定義分離時不能忘記加這一行模板了,否則會出現:

3.2類模板的實例化

Stack<int> st1; ???// int
Stack<double> st2; // double

由于類模板不能通過隱式實例化來確定形參類型,所以必須用顯式實例化,且注意:Stack是類名,Stack<int>是類型。

其次再補充一下之前的:

template<class T>
void Stack<T>::Push(const T& data)
{// 擴容_array[_size] = data;++_size;
}

模板參數不能是憑空出來的,故指定類域得加模板參數,否則T是用不了的,因為模板是給Stack類用的模板參數,因為模板參數是屬于這個Stack類型,因為T可以為任意類型,若去掉,則本質上Push要實例化出多個,這樣就不行了。

4.總結

這是模板初階的東西,所以很多東西是沒有講解完全的,需要到模板進階來講清楚,但是模板進階要很晚了,下面這張圖片是C++初階的全部內容,所以我們現在已經C++初階過了一半了,之后難度會比較高(第8節開始),所以需要深入學習也需要持之以恒哦!

加油吧,相信自己能學會才是最好的安慰!喜歡的可以一鍵三連哦,下一節很簡單,所以應該很快就發出來的!

?

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

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

相關文章

「Mac暢玩AIGC與多模態02」部署篇01 - 在 Mac 上部署 Ollama + Open WebUI

一、概述 本篇介紹如何在 macOS 環境下本地部署 Ollama 推理服務,并通過 Open WebUI 實現可視化交互界面。該流程無需 CUDA 或專用驅動,適用于 M 系列或 Intel 芯片的 Mac,便于快速測試本地大語言模型能力。 二、部署流程 1. 環境準備 安裝 Homebrew(如尚未安裝):/bin…

JavaScript 中 undefined 和 not defined 的區別

在 JavaScript 的調試過程中&#xff0c;你是否經常看到 undefined 卻不知其來源&#xff1f;是否曾被 ReferenceError: xxx is not defined 的錯誤提示困擾&#xff1f;這兩個看似相似的概念&#xff0c;實際上是 JavaScript 類型系統中最重要的分水嶺。本文將帶你撥開迷霧&am…

django admin AttributeError: ‘UserResorce‘ object has no attribute ‘ID‘

在 Django 中遇到 AttributeError: ‘UserResource’ object has no attribute ‘ID’ 這類錯誤通常是因為你在代碼中嘗試訪問一個不存在的屬性。在你的例子中&#xff0c;錯誤提示表明 UserResource 類中沒有名為 ID 的屬性。這可能是由以下幾個原因造成的&#xff1a; 拼寫錯…

對鴻蒙 Next 系統“成熟論”的深度剖析-優雅草卓伊凡

對鴻蒙 Next 系統“成熟論”的深度剖析-優雅草卓伊凡 在科技飛速發展的當下&#xff0c;鴻蒙 Next 系統無疑成為了眾多科技愛好者與行業人士關注的焦點。今日&#xff0c;卓伊凡便收到這樣一個饒有趣味的問題&#xff1a;鴻蒙 Next 系統究竟需要多長時間才能完全成熟&#xff…

快速上手GO的net/http包,個人學習筆記

更多個人筆記&#xff1a;&#xff08;僅供參考&#xff0c;非盈利&#xff09; gitee&#xff1a; https://gitee.com/harryhack/it_note github&#xff1a; https://github.com/ZHLOVEYY/IT_note 針對GO中net/http包的學習筆記 基礎快速了解 創建簡單的GOHTTP服務 func …

AI-Browser適用于 ChatGPT、Gemini、Claude、DeepSeek、Grok的客戶端開源應用程序,集成了 Monaco 編輯器。

一、軟件介紹 文末提供程序和源碼下載學習 AI-Browser適用于 ChatGPT、Gemini、Claude、DeepSeek、Grok、Felo、Cody、JENOVA、Phind、Perplexity、Genspark 和 Google AI Studio 的客戶端應用程序&#xff0c;集成了 Monaco 編輯器。使用 Electron 構建的強大桌面應用程序&a…

Dify框架面試內容整理-Dify如何處理知識庫的集成?

Dify 在知識庫集成方面采用了“檢索增強生成(RAG)”的技術架構,核心實現思路如下: 一、知識庫集成的整體流程 Dify處理知識庫集成通常包括以下關鍵步驟: 文檔上傳↓

Laravel 模型使用全局作用域和局部作用域

一. 需要解決什么問題 最近Laravel 項目中遇到一個需求&#xff0c;我有一個客戶表&#xff0c;每個員工都有自己的客戶&#xff0c;但是自己只能看自己的客戶。 項目中&#xff0c;有很多功能需要查詢客戶列表&#xff0c;客戶詳情&#xff0c;查詢客戶入口很多&#xff0c;…

【Nova UI】十二、打造組件庫之按鈕組件(上):邁向功能構建的關鍵一步

序言 在上一篇文章中&#xff0c;我們深入探索了 icon 組件從測試到全局注冊的全過程&#x1f3af;&#xff0c;成功為其在項目中穩定運行筑牢了根基。此刻&#xff0c;組件庫的建設之旅仍在繼續&#xff0c;我們將目光聚焦于另一個關鍵組件 —— 按鈕組件。按鈕作為用戶與界面…

鴻蒙OSS文件(視頻/圖片)壓縮上傳組件-能夠增刪改查

一、鴻蒙實現處理-壓縮上傳整體代碼處理邏輯 轉沙箱壓縮獲取憑證并上傳文件 文件準備&#xff08;拿到文件流&#xff09;獲取上傳憑證&#xff08;調接口1拿到file_name和upload_url&#xff09;執行文件上傳&#xff08;向階段2拿到的upload_url上傳文件&#xff09;更新列表…

河道流量監測,雷達流量計賦能水安全智慧守護

在蜿蜒的河道之上&#xff0c;水流的脈搏始終與人類文明的興衰緊密相連。從農田灌溉的水量調配到城市防洪的精準預警&#xff0c;從生態保護的水質溯源到水資源管理的決策&#xff0c;河道流量監測如同大地的 “血管檢測”&#xff0c;是守護水安全的第一道防線。傳統監測手段在…

CSS3 基礎(邊框效果)

一、邊框效果 屬性功能示例值說明border-radius創建圓角border-radius: 20px;設置元素的圓角半徑&#xff0c;支持像素&#xff08;px&#xff09;或百分比&#xff08;%&#xff09;。值為 50% 時可變為圓形。box-shadow添加陰影box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.5)…

零基礎小白如何上岸數模國獎

零基礎小白如何上岸數模國獎 我自己本人第一次參加數模國賽順利上岸國獎&#xff0c;當然那段經歷也是比較痛苦了&#xff0c;差不多也是從當年四月開始接觸數學建模&#xff0c;第一次參加媽媽杯成績并不理想&#xff0c;后面不斷參加數模比賽進行模擬&#xff0c;最后順利上岸…

SQL學習-常用函數

常見SQL函數使用 &#xff08;注意&#xff1a;不同的數據庫類型使用的語法不同&#xff09; 以下是MySQL和PostgreSQL在實現替換、抽取、拼接、分列四個常見字符串操作功能時的核心區別總結&#xff0c;按功能分類對比&#xff1a; 1. 替換&#xff08;Replace&#xff09; …

rt-linux下的cgroup cpu的死鎖bug

一、背景 rt-linux系統有其非常大的實時性的優勢&#xff0c;但是與之俱來的是該系統上有一些天然的缺陷。由于rt-linux系統允許進程在內核態執行的邏輯里&#xff0c;在持鎖期間&#xff0c;甚至持spinlock鎖期間&#xff0c;都能被其他進程搶占。這一特性能帶來實時性的好處…

java—12 kafka

目錄 一、消息隊列的優缺點 二、常用MQ 1. Kafka 2. RocketMQ 3. RabbitMQ 4. ActiveMQ 5. ZeroMQ 6. MQ選型對比 適用場景——從公司基礎建設力量角度出發 適用場景——從業務場景角度出發 四、基本概念和操作 1. kafka常用術語 2. kafka常用指令 3. 單播消息&a…

14【模塊學習】74HC595:使用學習

74HC595 1、74HC595簡介2、代碼演示2.1、驅動8位流水燈 3、74HC595級聯3.1、驅動16位流水燈3.2、驅動8位數碼管3.3、驅動8x8點陣屏幕3.4、8x8點陣屏幕滾動顯示 1、74HC595簡介 在51單片機中IO引腳資源十分的緊缺&#xff0c;所以常常需要使用75HC595芯片進行驅動那些需要占用多…

JAVA后端開發常用的LINUX命令總結

一、Linux常用命令大全&#xff08;2025年最新版&#xff09; 常用 Linux 命令 文件和目錄管理&#xff1a; cd&#xff1a;用于切換當前工作目錄&#xff0c;如cd /home/user。mkdir&#xff1a;創建新目錄&#xff0c;mkdir -p /home/user/mydir可遞歸創建多級目錄。pwd&am…

uniapp-商城-40-shop 購物車 選好了 進行訂單確認4 配送方式3 地址編輯

前面說了配送 和地址頁面 當地址頁面為空或需要添加地址時&#xff0c;需要添加地址。 我的地址頁面有個按鈕 就是添加地址 點擊 添加地址 按鈕 后&#xff0c;就會跳轉到地址添加的頁面 1、添加地址頁面 2、添加地址文件夾以及文件的創建 3、添加地址的代碼 <template…

現場問題排查-postgresql某表索引損壞導致指定數據無法更新影響卷宗材料上傳

問題現象 今天突然被拉進一個群&#xff0c;說某地區友商推送編目結果報錯&#xff0c;在我們自己的卷宗系統上傳材料也一直轉圈&#xff0c;也刪除不了案件卷宗&#xff0c;重置模板也沒用&#xff0c;只有個別案件有問題。雖然這事兒不屬于我負責&#xff0c;但還是抽時間給…