【C++破局】泛型編程|函數模板|類模板

?作者主頁

📚lovewold少個r博客主頁

? ???本文重點c++模板初階知識點講解

👉【C-C++入門系列專欄】博客文章專欄傳送門

😄每日一言:花有重開日,人無再少年

目錄

前言

泛型編程

函數模板

函數模板概念

函數模板格式

函數模板的原理

函數模板的實例化

模板參數的匹配原則

類模板

類模板的定義格式

類模板的實例化

總結


前言

? ? ? ? C++是一門面向對象的語言,很多情況下我們不需要在編寫程序時候去過多的考慮底層。而在前面我們學習C++的輸入輸出好像就是這樣子,編譯器會自動幫我們做很多的事情,而不需要自己去傳遞輸入輸出的變量類型等因素。這種方式肯定對于編寫程序的人來講是輕松的。世界上的各種科技的進步其實都離不開人對于懶惰的追求,對于方便的執著,而今天我要講解的模板好像就算是一種特殊的產物。

? ? ? ? 首先我們先寫一個比較簡單的程序來細致的探究一下我們為何需要模板。這里我們寫了一個用于整形變量交換的函數,交換函數無論是在什么排序亦或者一些計算的時候都是常用函數。

void swap(int& x, int& y)
{int temp = x;x = y;y = temp;
}
int main()
{int a = 10;int b = 5;cout << "a=" << a << " " << "b=" << b << endl;swap(a, b);cout << "swap~" << endl;cout << "a=" << a << " " << "b=" << b << endl;return 0;
}

????????問題來了,我們這里需要特別強調這里是用于整形類型的交換函數,因此這對于其他類型變量并不合適。那么我們如何去實現長整形,短整形,浮點型,字符型······。函數重載?

????????C++提供了函數重載的方式,對于一些傳參會直接以其傳遞參數而決定對應的函數。

????????但是缺陷也很明顯,我們的函數僅僅是類型不同,那么多類型我們都需要去重載么。亦或者我們先創建我們需要的類型,等到新類型出現的時候,用戶再自己去增加對應的函數重載么。

????????另一個關鍵點是,我們這里僅僅只是交換函數,比較簡單,而對于一份各種函數相互嵌套的代碼,一份代碼出錯,其他重載函數全得改,代碼的可維護性比較低。

泛型編程

? ? ? ? 我們再看先前的代碼,會發現僅僅只是類型的不同罷了,我們可不可以提供一種方式和cout以及cin一樣,把類型識別的任務交給編譯器去完成,自己只需要給他傳遞參數變量即可。也就是說我們只需要提供一份代碼作為模具,編譯器可以根據不同的類型利用這個模具生成相應的代碼。

void swap(int& x, int& y)
{int temp = x;x = y;y = temp;
}
void swap(double& x, double& y)
{double temp = x;x = y;y = temp;
}
void swap(char& x, char& y)
{char temp = x;x = y;y = temp;
}
int main()
{int a = 10;int b = 5;cout << "a=" << a << " " << "b=" << b << endl;swap(a, b);cout << "swap~" << endl;cout << "a=" << a << " " << "b=" << b << endl;return 0;
}

????????無論是活字印刷術還是現在的模具澆筑技術,其根本的目的就是維持其功能一致即可,你可以注入不同的材料以改變其最后成品的效果,但本質上實現的功能是一樣的,外形是一致的。

? ? ? ? 之所以cv工程師能有獨特的cv大法,也是因為前人擁有特定的已經可以使用的板子,而只需要去改吧改吧然后切合自己的工程內容即可實現一個全新的項目成果。這也就是一種代碼復用的常規手段而已。

? ? ? ? ?話不多說,接下來直入正題。我們先談一談何為泛型編程。

????????泛型編程是一種編程范式,其目標是編寫與特定數據類型無關的通用代碼,以便更廣泛地重用代碼。泛型編程使得程序員可以編寫與數據類型無關的算法和數據結構,從而提高代碼的靈活性、可重用性和可維護性。

????????具體而言,泛型編程通過使用參數化類型(parameterized types)來實現。參數化類型是一種允許在代碼中使用未指定具體類型的抽象類型。這樣,可以編寫算法和數據結構,而不必在編寫時指定具體的數據類型。在需要使用這些算法和數據結構的地方,可以通過提供具體的類型來實現參數的具體化。

????????在C++中,泛型編程主要通過模板來實現。模板允許程序員編寫與數據類型無關的代碼,可以用于不同的數據類型。這使得在不同的上下文中重用代碼成為可能。例如,可以編寫通用的排序算法、容器類、以及其他算法和數據結構,而不必為每種數據類型都編寫一套特定的代碼。而模板就是泛型編程的基礎。

函數模板

函數模板概念

????????函數模板是C++中用于創建通用函數的一種機制,允許程序員編寫與特定數據類型無關的函數代碼。函數模板通過使用參數化類型來實現,使得可以在編寫代碼時使用未指定具體類型的抽象類型這樣,函數模板可以適用于多種數據類型,提高了代碼的靈活性和重用性。

函數模板格式

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

template T 表示模板參數,T是一個占位符,代表任意數據類型。在函數模板中,可以使用T作為函數的參數類型、返回類型,以及在函數體中進行通用的操作。

注意:typename是用來定義模板關鍵字,也可以使用class不能使用struct代替class

函數模板的原理

? ? ? ? 人類從農業時期到工業時期,很多重復機械的工作直接交給了機器去完成,極大的解放的生產力。機器生產淘汰了很多手工創作的東西,其本質上就是把這些工作交給了機器去完成。

? ? ? ? 函數模板本身并不是一個函數,而是像類一樣作為一個藍圖,是編譯器用使用方式產生具體類型函數的一個模具。

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

函數模板的實例化

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

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

template <class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10;int a2 = 5;Add(a1, a2);double b1 = 10.0;double b2 = 5.0;Add(b1, b2);return 0;
}

????????而當調用的時候將不同類型混合傳參是不被允許的,因為在編譯期間編譯器需要推演,對于a1可以推演為int,b1可以推演為double,但是模板參數列表中只有一個T,編譯器無法確定T被換為int還是double而報錯。在模板中,編譯器一般不會去做類型轉換,否則出錯了編譯器就會承擔不小的后果。

????????因此對于這種問題我們通常有兩種方式進行解決:用戶自己進行強制類型轉換或者使用顯示實例化

顯示實例化:在函數名的后面指定模板參數的實際類型

int main()
{int a1 = 10;int a2 = 5;Add(a1, a2);double b1 = 10.0;double b2 = 5.0;Add(b1, b2);Add<int>(a1, b1);//顯示實例化return 0;
}

模板參數的匹配原則

????????一個非模板函數和模板函數可以同時存在,而且該模板函數還可以實例化為這個非模板函數。(這種實例化為非模板函數指在參數傳遞上可能維持一樣,但是依據優先匹配的原則,在調用函數的時候可以做一些特殊處理)。這里我們通過不同函數之間打印信息而進行調用優先級的查看。

//專門處理整形類型加法函數
int Add(int left, int right)
{cout << "非模板函數" << endl;return left + right;
}
//通用加法模板
template<class T>
T Add(T left, T right)
{cout << "模板函數" << endl;return left + right;
}
void test()
{Add(1,2);//與非模板函數優先匹配Add(1.0, 2.0);//模板函數Add<int>(1, 2);//調用特定版本的Add版本,走模板函數
}
int main()
{test();return 0;
}

????????對于非模板函數與同名函數模板,如果有其他條件相同,在調用的時候會優先去調用非函數模板而不會從該函數模板中生成實例化函數,如果一個函數可以產生一個具有更好匹配的函數,那么選擇模板。

int Add(int left, int right)
{cout << "非模板函數" << endl;return left + right;
}
//通用加法模板
template<class T1 , class T2>
T1 Add(T1 left, T2 right)
{cout << "模板函數" << endl;return left + right;
}
void test()
{Add(1, 2);//與非模板函數優先匹配Add(1, 2.0);//具備更加匹配的版本而不需要類型轉換,編譯器優先生成更加匹配的Add函數版本
}
int main()
{test();return 0;
}

????????模板函數不能進行自動類型轉換(幫你推演就夠忙了,類型轉換發生錯誤你還得罵他自然就不會幫你做這件沒意義的事情)。但是普通函數可以進行自動類型轉換

類模板

類模板的定義格式

template<class T1, class T2,class T3>//參數列表可以定義多個模板變量
class 類模板名
{//類成員定義
};

????????我們前面學習順序表提到過一點,使用typedef對類型名進行重命名。C語言中的類型重命名是指通過使用typedef關鍵字來為已有的類型創建一個新的別名。這樣可以簡化代碼,提高可讀性,并且方便批量修改具體類型,便于維護代碼。但是當我們學習了類模板之后我們發現我們并不需要這樣做,而是使用類模板。這里我們簡要的構造一個動態順序表的類模板來體會。

template<class T>
class Vector
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}~Vector();// 其他成員函數在這里...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;
}

類模板的實例化

????????類模板的實例化與函數模板實例化不同,類模板實例化需要在類模板的名字前面跟<>,然后需要講實例化的類型放在<>中即可(就像必須顯式的實例化)。類模板名字不是真正的類,而實例化的結果才是真正的類(藍圖和用藍圖進行建筑的關系)。實例化時,編譯器會生成針對具體數據類型的類定義,從而使得類模板變得具體化,可以像普通類一樣使用。

Vector類名,Vector<int>才是類型
Vector<int> s1;
Vector<double> s2;

總結

????????泛型編程是一種編程范式,其目標是編寫可重用、通用的代碼,以便能夠適應多種數據類型而無需針對每種類型重復編寫相似的代碼。在C++中,泛型編程主要通過函數模板和類模板來實現。其優勢如下:

  • 提高代碼的重用性和可維護性。

  • 允許在不同數據類型上進行抽象,減少代碼冗余。

函數模板(Function Templates)

概念:函數模板是一種定義通用函數的方式,其中函數的參數或返回類型可以是通用的類型參數。

語法:

template <class T> //typename也可以
T Add(T a, T b) 
{return a + b;
}

實例化:通過指定具體的數據類型,編譯器會生成對應類型的函數定義。

實例化用法:

int r_int = Add(3, 4);          // 實例化為 Add<int>(3, 4)
double r_double = Add(3.14, 2.5);// 實例化為 Add<double>(3.14, 2.5)

類模板(Class Templates)

概念:類模板是一種定義通用類的方式,其中類的成員或行為可以依賴于通用的類型參數。

語法:
?

? ?template <class T> class MyClass {public:MyClass(T value) : data(value) {}void play() { /*  ... */ }private:T data;};

實例化:通過指定具體的數據類型,編譯器會生成對應類型的類定義。

使用:

? ?MyClass<int> intObject(42);MyClass<double> doubleObject(3.14);

? ? 作者水平有限,如有錯誤歡迎指正!


? ??

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

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

相關文章

用Java實現簡易的圖書管理系統(超詳細)

目錄 1.設計背景 2.設計思路 3.模塊展示及代碼演示 3.1 Book類的實現 3.2 BookList類的實現(書架) 3.3 異常類的實現(OperationException) 3.4 用戶類的實現 3.5 操作接口的實現(定義規范) 3.6 操作類的具體實現 3.6.1 增加操作 3.6.2 查找操作 3.6.3 刪除操作 3.6…

標簽打印機打印標簽時出現,數據處理過程中錯誤 無法設置項目 圖片1的內容無法打印

環境&#xff1a; Win10專業版 NiceLabel Designer 10.1 問題描述&#xff1a; 標簽打印機打印標簽時出現&#xff0c;數據處理過程中錯誤 無法設置項目 圖片1的內容無法打印 解決方案&#xff1a; 1.刪除標簽部分文字打印測試 還是一樣&#xff08;未解決&#xff09; …

已解決java.lang.IllegalStateException異常的正確解決方法,親測有效!!!

已解決java.lang.IllegalStateException異常的正確解決方法&#xff0c;親測有效&#xff01;&#xff01;&#xff01;文章目錄 報錯問題解決思路解決方法交流 報錯問題 java.lang.IllegalStateException 解決思路 java.lang.IllegalStateException通常表示程序的當前狀態與…

jenkins 參數構建

應用保存 [rootjenkins-node1 .ssh]# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved i…

【嵌入式面試】2022年嵌入式經典面試題匯總(C語言)

&#x1f4dc;作者&#xff1a;不想脫發的基兄 &#x1f4fa;專欄&#xff1a;《嵌入式面試》 &#x1f4e3;格言&#xff1a;不管前方的路有多苦&#xff0c;只要走的方向正確&#xff0c;不管多么崎嶇不平&#xff0c;都比站在原地更接近幸福。 前言&#xff1a; 2022年秋招我…

C++之初始化列表詳細剖析

一、初始化列表定義 初始化列表&#xff1a;以一個冒號開始&#xff0c;接著是一個以逗號分隔的數據成員列表&#xff0c;每個"成員變量"后面跟一個放在括號中的初始值或表達式。 class Date { public:Date(int year, int month, int day): _year(year), _month(mont…

OpenCV快速入門:圖像分析——圖像分割和圖像修復

文章目錄 前言一、圖像分割1.1 漫水填充法1.1.1 漫水填充法原理1.1.2 漫水填充法實現步驟1.1.3 代碼實現 1.2 分水嶺法1.2.1 分水嶺法原理1.2.2 分水嶺法實現步驟1.2.3 代碼實現 1.3 GrabCut法1.3.1 GrabCut法原理1.3.2 GrabCut法實現步驟1.3.3 代碼實現 1.4 Mean-Shift法1.4.1…

論文閱讀 (106):Decoupling maxlogit for out-of-distribution detection (2023 CVPR)

文章目錄 1 概述1.1 要點1.2 代碼1.3 引用 2 預備知識3 方法3.1 MaxLogit3.2 改進MaxCosine和MaxNorm3.3 DML 1 概述 1.1 要點 題目&#xff1a;解耦最大logit分布外檢測 (Decoupling maxlogit for out-of-distribution detection) 方法&#xff1a; 提出了一種心機基于log…

多級緩存快速上手

哈嘍~大家好&#xff0c;這篇來看看多級緩存。 &#x1f947;個人主頁&#xff1a;個人主頁????? &#x1f948; 系列專欄&#xff1a;【微服務】 &#x1f949;與這篇相關的文章&#xff1a; JAVA進程和線程JAVA進程和線程-CSDN博客Http…

不做機器視覺工程師,轉行,轉崗的建議與想法

正所謂外行看熱鬧&#xff0c;內行看門道。提前咨詢前輩們&#xff0c;多問問&#xff0c;多看看。要做就做&#xff0c;一定要提前做好防范。 無論你是要轉行或者是轉崗&#xff0c;看你有沒有本錢和試錯成本 有些人&#xff0c;家庭好&#xff0c;可以一直去試錯和從頭再來。…

無線WiFi安全滲透與攻防(國外篇):使用 Aircrack-ng 破解 WEP 密碼

使用 Aircrack-ng 破解 WEP 密碼 使用 Aircrack-ng 破解 WEP 密碼一. 用 Aircrack-ng 破解 WEP 密碼 - 背景知識網卡與網卡芯片WEP 加密協議WEP 所使用的身份認證協議二. 使用 Aircrack-ng 破解 WEP 密碼 - 破解原理破解機理三. 使用 Aircrack-ng 破解 WEP 密碼 - aircrack-ng …

學習.NET驗證模塊FluentValidation的基本用法(續1:其它常見用法)

FluentValidation模塊支持鏈式驗證方法調用&#xff0c;也就是說&#xff0c;除了 RuleFor(r > r.UserName).NotEmpty()調用方式之外&#xff0c;還可以將對單個屬性的多種驗證函數以鏈式調用方式串接起來&#xff0c;比如UserName屬性不能為空&#xff0c;長度在5~10之間&a…

__attribute__((constructor))用法解析

__attribute__((constructor))是GCC和兼容的編譯器中的一個特性&#xff0c;用于指示編譯器將一個函數標記為在程序啟動時自動執行的初始化函數。 同樣的還有__attribute__((destructor))在main()函數后調用。 當你在一個函數聲明或定義前加上__attribute__((constructor))屬…

淺談 Guava 中的 ImmutableMap.of 方法的坑

作者&#xff1a;明明如月學長&#xff0c; CSDN 博客專家&#xff0c;大廠高級 Java 工程師&#xff0c;《性能優化方法論》作者、《解鎖大廠思維&#xff1a;剖析《阿里巴巴Java開發手冊》》、《再學經典&#xff1a;《EffectiveJava》獨家解析》專欄作者。 熱門文章推薦&…

vue項目下.env.development環境變量配置文件

.env.development 文件是一個用于開發環境配置的文件。在許多應用程序中&#xff0c;開發環境和生產環境具有不同的配置需求。.env.development 文件允許你在開發環境中定義特定的環境變量和配置選項。 一般來說&#xff0c;.env.development 文件用于存儲開發環境相關的配置信…

國自然項目基金撰寫的隱藏技巧、范例分析及提交前的自我審查

目錄 一、基金項目申請要求、重點及項目介紹 二、基金的撰寫技巧 三、基金撰寫的隱藏技巧 四、范例分析及提交前的自我審查 更多應用 基金項目申請需要進行跨學科的技術融合&#xff0c;申請人需要與不同領域結合&#xff0c;形成多學科交叉的研究。基金項目申請在新時期更…

由紅黑樹引出的HashMap擴容機制的思考

紅黑樹是什么&#xff1f; 三大特點&#xff1a; 根節點是黑色&#xff0c;葉節點是不存儲數據的黑色空節點 任何相鄰的兩個節點不能同時為紅色 任意節點到其可到達的節點間包含相同數量的黑色節點 聯想&#xff1a;Java HashMap底層紅黑樹原理 HashMap基于哈希表Map接口實…

快速掌握Pyqt5的三種主窗口

PyQt5是一個強大的跨平臺GUI框架&#xff0c;它提供了多種不同類型的主窗口類&#xff0c;以滿足不同的應用需求。下面是PyQt5中最常見的幾種主窗口類型及其創建方式的簡介&#xff1a; 1. QMainWindow QMainWindow是用于創建具有菜單欄、工具欄、狀態欄和中心窗口部件&#…

內存池 示例一

內存池是一種管理內存分配和釋放的技術&#xff0c;用于優化內存的使用效率。它通過預先分配一塊內存區域&#xff0c;并將其劃分為多個較小的塊&#xff08;內存塊池&#xff09;&#xff0c;然后按需分配這些內存塊來減少內存碎片化和頻繁的系統調用。這些內存塊可以是相同大…

Centos7.9配置nfs共享及rsync同步

客戶需求對oracle數據庫做一個跨機房的備份&#xff0c;原環境已做rman備份和每天expdp全庫導出&#xff0c;遠端只有虛擬化環境&#xff0c;可提供一個虛擬機&#xff0c;2個機房間網絡互通。 首先配置nfs服務端 查看操作系統版本 [rootnas199 ~]# more /etc/redhat-relea…