C++底層學習預備:模板初階

文章目錄

  • 1.編程范式
  • 2.函數模板
    • 2.1 函數模板概念
    • 2.2 函數模板原理
    • 2.3 函數模板實例化
      • 2.3.1 隱式實例化
      • 2.3.2 顯式實例化
    • 2.4 模板參數的匹配原則
  • 3.類模板
  • 希望讀者們多多三連支持
  • 小編會繼續更新
  • 你們的鼓勵就是我前進的動力!

進入STL庫學習之前我們要先了解有關模板的學習,以便在學習完STL庫使用之后,能更深入的了解其底層工作原理

1.編程范式

編程范式指的是我們使用編程的基本風格和方法
常見的方式有以下幾種:

面向對象編程(OOP)

將數據和操作數據的方法封裝在類中,通過類的實例(對象)來進行交互,強調數據的封裝、繼承和多態性

定義一個Shape基類,包含計算面積的純虛函數,再派生出CircleRectangle等類,重寫計算面積的函數,體現了面向對象的繼承多態特性

函數式編程

將計算視為函數的組合和應用,強調不可變數據和純函數,避免副作用,注重函數的輸入輸出關系

使用std::functionlambda表達式可以方便地進行函數式編程,如用lambda表達式定義一個簡單的加法函數,不修改外部狀態,只返回計算結果

過程式編程

以過程(函數)為中心,將程序分解為一系列的步驟和函數調用,數據和操作數據的函數相對獨立

傳統的C語言風格的編程方式,如編寫一個計算階乘的函數,通過循環遞歸來實現計算過程,就是典型的過程式編程

泛型編程

定義函數、類或其他程序結構時,不指定具體的數據類型,而是使用類型參數來代表未知的數據類型

algorithm頭文件中的swap函數就是一種常見的泛式編程,他不指定任何類型就能實現交換,依靠的就是泛式編程,也是我們接下來要學習的模板

2.函數模板

在還不知道頭文件前實現swap函數通常是這樣的:

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

為了符合各個場景下實現參數互換,要對同一個函數實現不同類型的函數重載,這種方式固然可行,但是每個類型都寫一遍太過于冗余了

  1. 重載的函數僅僅是類型不同,代碼復用率比較低,只要有新類型出現時,就需要用戶自己增加對應的函數
  2. 代碼的可維護性比較低,一個出錯可能所有的重載均出錯

2.1 函數模板概念

我們知道文字的印刷是依靠活字印刷術的模板實現的,那能否告訴編譯器一個模子,讓編譯器根據不同的類型利用該模子來生成代碼呢?

這里用到的模板就是函數模板其語法形式為:

template<typename T1, typename T2,......,typename Tn>

template就是模板的意思,是用來定義模板參數關鍵字,也可以使用class,切記:不能使用struct代替class,因為structclass的默認權限不同,會導致一些混淆和潛在的問題

2.2 函數模板原理

函數模板是一個藍圖,它本身并不是函數,是編譯器用使用方式產生特定具體類型函數的模具。所以其實模板就是將本來應該我們做的重復的事情交給了編譯器

舉個例子:

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

實現一個Swap交換函數

在這里插入圖片描述
對兩個不同類型的函數進行同一個函數的調用,調試模式下轉到反匯編可以發現,兩個函數式模板示例化后被調用的

這直接說明了調用的不是同一個函數

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

比如:當用double類型使用函數模板時,編譯器通過對實參類型的推演,將T確定為double類型,然后產生一份專門處理double類型的代碼,這個類型無論是內置類型還是自定義類型都可以

2.3 函數模板實例化

用不同類型的參數使用函數模板時,稱為函數模板的實例化

2.3.1 隱式實例化

讓編譯器根據實參推演模板參數的實際類型叫作隱式實例化

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);return 0;
}

正常情況下的調用就是隱式實例化

🔥值得注意的是: Add函數前加const是因為這里如果像下面例子一樣進行強制轉化會生成臨時變量,具有常性

該知識點在前面有提到過:

傳送門:C++命運石之門代碼抉擇:C++入門(中)

2.3.2 顯式實例化

在函數名后的<>中指定模板參數的實際類型叫作顯式實例化

Add(a1, d1);

還是上面的例子,如果既調用int,又調用double,到底是用哪種類型編譯器無法決定,就需要顯式實例化

🚩用戶自己來強制轉化

Add(a1, (int)d1);

🚩使用顯式實例化

Add<int>(a1, d1);

指定T的類型為int

這通常不是顯式實例化的常用場景,舉個例子:

template<class T>
T* Alloc(int n)
{return new T[n];
}int main()
{Alloc<int>(5);return 0;
}

如果寫成Alloc(5),編譯器不知道你要分配的是int數組double數組還是其他類型的數組,所以無法自動推導T的類型,這時候就需要顯式指定模板參數,像Alloc<int>(5) 這樣明確告訴編譯器T是int類型

2.4 模板參數的匹配原則

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

// 專門處理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的加法函數
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); // 模板函數可以生成更加匹配的版本,編譯器根據實參生成更加匹配的Add函}

🚩模板函數不允許自動類型轉換,但普通函數可以進行自動類型轉換

這里的自動轉化就是上面的實例化中的轉化,也要和auto自動推導區分開,不是同一個東西

3.類模板

類模板其實和函數模板是類似的

其語法形式為:

template<class T1, class T2, ..., class Tn>

因為類不像函數那樣語法上支持自動類型轉化,所以類模板調用必須顯式實例化

// 動態順序表
// 注意: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;
}int main()
{// Vector類名,Vector<int>才是類型Vector<int> s1;Vector<double> s2;return 0;
}

我們在寫模板類時盡量不要聲明定義分離,原因有些復雜放在模板進階的時候講,如果一定分離的話要注意:

  1. 對于普通類,類名和類型一樣
  2. 對于模板類Vector類名Vector<int>才是類型

希望讀者們多多三連支持

小編會繼續更新

你們的鼓勵就是我前進的動力!

請添加圖片描述

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

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

相關文章

【玩轉 Postman 接口測試與開發2_015】第12章:模擬服務器(Mock servers)在 Postman 中的創建與用法(含完整實測效果圖)

《API Testing and Development with Postman》最新第二版封面 文章目錄 第十二章 模擬服務器&#xff08;Mock servers&#xff09;在 Postman 中的創建與用法1 模擬服務器的概念2 模擬服務器的創建2.1 開啟側邊欄2.2 模擬服務器的兩種創建方式2.3 私有模擬器的 API 秘鑰的用法…

【算法】回溯算法專題③ ——排列型回溯 python

目錄 前置小試牛刀回歸經典舉一反三總結 前置 【算法】回溯算法專題① ——子集型回溯 python 【算法】回溯算法專題② ——組合型回溯 剪枝 python 小試牛刀 全排列 https://leetcode.cn/problems/permutations/description/ 給定一個不含重復數字的數組 nums &#xff0c;返…

8.原型模式(Prototype)

動機 在軟件系統中&#xff0c;經常面臨著某些結構復雜的對象的創建工作&#xff1b;由于需求的變化&#xff0c;這些對象經常面臨著劇烈的變化&#xff0c;但是它們卻擁有比較穩定一致的接口。 之前的工廠方法和抽象工廠將抽象基類和具體的實現分開。原型模式也差不多&#…

LabVIEW如何高頻采集溫度數據?

在LabVIEW中進行高頻溫度數據采集時&#xff0c;選擇合適的傳感器&#xff08;如熱電偶或熱電阻&#xff09;和采集硬件是關鍵。下面是一些建議&#xff0c;幫助實現高效的溫度數據采集&#xff1a; 1. 傳感器選擇&#xff1a; 熱電偶&#xff08;Thermocouple&#xff09;&am…

Kotlin 委托詳解

Kotlin 委托詳解 引言 Kotlin 作為一種現代化的編程語言&#xff0c;在 Android 開發等領域得到了廣泛的應用。在 Kotlin 中&#xff0c;委托&#xff08;Delegation&#xff09;是一種強大的特性&#xff0c;它可以讓我們以更簡潔的方式實現代碼的復用和擴展。本文將詳細解析…

npm 和 pip 安裝中常見問題總結

安裝路徑的疑惑&#xff1a;NPM 和 PIP 的安裝機制 NPM 安裝路徑規則&#xff1a; 依賴安裝在項目目錄下&#xff1a; 當你運行 npm install --save-dev jest&#xff0c;它會在當前目錄&#xff08;例如 F:\&#xff09;下創建一個 node_modules 文件夾&#xff0c;把 jest 安…

人工智能:農業領域的變革力量

在當今科技飛速發展的時代&#xff0c;人工智能正以前所未有的態勢滲透進各個領域&#xff0c;農業也不例外。想象一下&#xff0c;未來的農田里&#xff0c;農民不再是彎腰勞作的形象&#xff0c;而是坐在高科技的“智能農場”里&#xff0c;悠閑地喝著咖啡&#xff0c;指揮著…

LLM的Deep Research功能:重構人類認知與創新的新范式

在人工智能迅速發展的今天&#xff0c;大語言模型&#xff08;LLM&#xff09;的deep research功能正在成為重構人類認知方式的關鍵力量。 這一突破性的技術進展不僅帶來了工具層面的革新&#xff0c;更深刻地觸及了人類認知能力的本質。 本文將從認知科學的視角出發&#xf…

【Cadence仿真技巧學習筆記】求解65nm庫晶體管參數un, e0, Cox

在設計放大器的第一步就是確定好晶體管參數和直流工作點的選取。通過閱讀文獻&#xff0c;我了解到L波段低噪聲放大器的mos器件最優寬度計算公式為 W o p t . p 3 2 1 ω L C o x R s Q s p W_{opt.p}\frac{3}{2}\frac{1}{\omega LC_{ox}R_{s}Q_{sp}} Wopt.p?23?ωLCox?Rs…

前端力扣刷題 | 6:hot100之 矩陣

73. 矩陣置零 給定一個 m x n 的矩陣&#xff0c;如果一個元素為 0 &#xff0c;則將其所在行和列的所有元素都設為 0 。請使用 原地 算法。 法一&#xff1a; var setZeroes function(matrix) {let setX new Set(); // 用于存儲需要置零的行索引let setY new Set(); //…

每日一題——有效括號序列

有效括號序列 題目描述數據范圍&#xff1a;復雜度要求&#xff1a; 示例題解代碼實現代碼解析1. 定義棧和棧操作2. 棧的基本操作3. 主函數 isValid4. 返回值 時間和空間復雜度分析 題目描述 給出一個僅包含字符 (, ), {, }, [, ] 的字符串&#xff0c;判斷該字符串是否是一個…

集合通訊概覽

&#xff08;1&#xff09;通信的算法 是根據通訊的鏈路組成的 &#xff08;2&#xff09;因為通信鏈路 跟硬件強相關&#xff0c;所以每個CCL的庫都不一樣 芯片與芯片、不同U之間是怎么通信的&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 很重要…

紅黑樹的封裝

一、封裝思路 在 STL 中 map set 的底層就是封裝了一棵紅黑樹。 其中連接紅黑樹和容器的是迭代器&#xff0c;map set 暴露出的接口都不是自己寫的&#xff0c;而是紅黑樹寫的&#xff0c;外部接口封裝紅黑樹接口。 所以寫出紅黑樹為 map set 寫的接口&#xff0c;再在上層的…

java異常處理——try catch finally

單個異常處理 1.當try里的代碼發生了catch里指定類型的異常之后&#xff0c;才會執行catch里的代碼&#xff0c;程序正常執行到結尾 2.如果try里的代碼發生了非catch指定類型的異常&#xff0c;則會強制停止程序&#xff0c;報錯 3.finally修飾的代碼一定會執行&#xff0c;除…

使用QMUI實現用戶協議對話框

使用QMUI實現用戶協議對話框 懶加載用于初始化 TermServiceDialogController 對象。 懶加載 lazy var 的作用 lazy var dialogController: TermServiceDialogController {let r TermServiceDialogController()r.primaryButton.addTarget(self, action: #selector(primaryC…

C++進階: 紅黑樹及map與set封裝

紅黑樹總結整理 紅黑色概述&#xff1a; 紅黑樹整理與AVL樹類似&#xff0c;但在對樹的平衡做控制時&#xff0c;AVL樹會比紅黑樹更嚴格。 AVL樹是通過引入平衡因子的概念進行對樹高度控制。 紅黑樹則是對每個節點標記顏色&#xff0c;對顏色進行控制。 紅黑樹控制規則&…

在Qt中,slots 關鍵字有什么用?

有下面的Qt代碼&#xff1a; #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent nullptr…

列表標簽(無序列表、有序列表)

無序列表 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head><…

Kanass基礎教程-創建項目

Kanass是一款國產開源免費的項目管理工具&#xff0c;工具簡潔易用&#xff0c;開源免費&#xff0c;之前介紹過kanass的一些產品簡介及安裝配置方法&#xff0c;本文就從如何創建第一個項目來開始kanass上手之旅吧。 1. 創建項目 點擊項目->項目添加 按鈕進入項目添加頁面…

【Numpy核心編程攻略:Python數據處理、分析詳解與科學計算】2.10 ndarray內存模型:從指針到緩存優化

2.10 ndarray內存模型&#xff1a;從指針到緩存優化 目錄 #mermaid-svg-p0zxLYqAnn59O2Xe {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-p0zxLYqAnn59O2Xe .error-icon{fill:#552222;}#mermaid-svg-p0zxLYqAnn59O…