c++階梯之模板初階

1. 泛型編程

void Swap(int& x, int& y)
{int tmp = x;x = y;y = tmp;
}void Swap(double& x, double& y)
{double tmp = x;x = y;y = tmp;
}void Swap(char& x, char& y)
{char tmp = x;x = y;y = tmp;
}
int main()
{int a = 10, b = 20;double c = 1.1, d = 2.2;char m = '#', n = '*';Swap(a, b);Swap(c, d);Swap(m, n);return 0;
}

我們看這段代碼,使用c++的函數重載技術實現了不同類型的交換。但這段代碼未免太過冗雜和繁瑣,我們寫了很多近似重復的東西。

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

那么我們能不能寫一個模具,讓各個類型的變量都能復用它呢?這就是我們這節要學的泛型編程之模板。就像下面這樣,給出一個模具,可以做出各種模型。

如果在C++中,也能夠存在這樣一個模具,通過給這個模具中填充不同材料(類型),來獲得不同材料的鑄件(即生成具體類型的代碼),那將會節省許多精力。

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

模板分為函數模板與類模板。


?

2. 函數模板?

2.1 概念

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

2.2 格式
?

template<typename T1, typename T2,......,typename Tn>
返回值類型 函數名(參數列表){}

譬如我們將上面的Swap函數模板化。

template<class T>
void Swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;
}int main()
{int a = 10, b = 20;double c = 1.1, d = 2.2;char m = '#', n = '*';Swap(a, b);Swap(c, d);Swap(m, n);cout << a << "  " << b << endl;cout << c << "  " << d << endl;cout << m << "  " << n << endl;return 0;
}

?

注意:typename是用來定義模板參數關鍵字,也可以使用class(切記:不能使用struct代替class)
?

?2.3 原理

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

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

2.4 函數模板的實例化

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

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);/*該語句不能通過編譯,因為在編譯期間,當編譯器看到該實例化時,需要推演其實參類型通過實參a1將T推演為int,通過實參d1將T推演為double類型,但模板參數列表中只有一個T,編譯器無法確定此處到底該將T確定為int 或者 double類型而報錯注意:在模板中,編譯器一般不會進行類型轉換操作,因為一旦轉化出問題,編譯器就需要背黑鍋Add(a1, d1);*/// 此時有兩種處理方式:1. 用戶自己來強制轉化 2. 使用顯式實例化Add(a, (int)d);return0;
}

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

int main(void)
{int a = 10;double b = 20.0;// 顯式實例化Add<int>(a, b);return 0;
}

?如果類型不匹配,編譯器會嘗試進行隱式類型轉換,如果無法轉換成功編譯器將會報錯。

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版本

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

// 專門處理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函
數
}

定義了一個Add函數的模板,有兩個模板參數類型,對于模板函數而言恰好,但對于非函數模板類型而言,需要隱式類型轉換,明顯模板函數更加匹配。?

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

3. 類模板

3.1 類模板定義格式?

template<class T1, class T2, ..., class Tn>
class 類模板名
{
// 類內成員定義
};
// 動態順序表
// 注意: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;
}

3.2 類模板的實例化

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

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

范例

#include<iostream>
using namespace std;
template<class T>
class Stack
{
public:Stack():_a(new T[10]), _size(0), _capacity(10){}void push(const T& x);void STPop();T STTop();~Stack();
private:T* _a;int _capacity;int _size;
};template<class T>
Stack<T>::~Stack()
{delete[] _a;_size = 0;_capacity = 0;
}
template<class T>
void Stack<T>::push(const T& x)
{_a[_size++] = x;
}
template<class T>
void Stack<T>::STPop()
{_size--;}
template<class T>
T Stack<T>::STTop()
{return _a[_size - 1];
}
int main()
{Stack<int> s1;s1.push(1);s1.push(2);s1.push(3);cout << s1.STTop() << endl;s1.STPop();cout << s1.STTop() << endl;s1.STPop();cout << s1.STTop() << endl;s1.STPop();return 0;
}

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

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

相關文章

《Spring Security 簡易速速上手小冊》第7章 REST API 與微服務安全(2024 最新版)

文章目錄 7.1 保護 REST API7.1.1 基礎知識詳解7.1.2 重點案例&#xff1a;使用 JWT 進行身份驗證和授權案例 Demo 7.1.3 拓展案例 1&#xff1a;API 密鑰認證案例 Demo測試API密鑰認證 7.1.4 拓展案例 2&#xff1a;使用 OAuth2 保護 API案例 Demo測試 OAuth2 保護的 API 7.2 …

讀書筆記-三國演義-夏侯惇

夏侯惇&#xff08;公元夏侯惇&#xff08;公元190年-公元220年&#xff09;&#xff0c;字元讓&#xff0c;沛國譙縣&#xff08;今安徽亳州市&#xff09;人&#xff0c;是中國東漢末年至三國時期魏國重要將領之一。他是曹操麾下的得力將領&#xff0c;以勇猛忠誠而聞名于世。…

linux安裝matlab獲取許可證

1.點擊許可證 2. 3. 4. 4.主機ID 打開linux輸入 /sbin/ifconfigether后邊的就是 6.計算機登錄名 打開linux輸入 whoami7. 8. 9.

局域網https自簽名教程

們的客戶是在內網環境里面&#xff0c;所以就只能用自簽名證書來搞&#xff0c;我一想這還不容易&#xff0c;就迅速的百度了一下隨便找了個文章開始照貓畫虎&#xff0c;很快就弄完了&#xff0c;但是弄完后發現還是有問題&#xff0c;而且https 還是報不安全&#xff0c; 1、…

(規劃)24屆春招和25屆暑假實習路線準備規劃

春招&&暑假實習&#xff1a; 1.八股&#xff1a; 可以去一些八股網站上面進行閱讀。 2.項目&#xff1a;至少準備1-2個項目&#xff0c;可以條理清晰的進行項目介紹和難點剖析。 3.算法&#xff1a; hot100 &#xff0c;劍指offer 能刷的很熟&#xff0c;算法關就差…

【R包報錯】使用sceasy包轉換rds文件與h5ad文件報錯PyType_GetFlags

想要將scanpy的h5ad文件轉為rds文件&#xff0c;有一個很方便的包sceasy可以使用&#xff0c;安裝簡單&#xff0c;代碼簡易。 安裝方式 # bioconda package:conda install -c bioconda r-sceasy# R package:devtools::install_github("cellgeni/sceasy")#額外需要…

android系統開發之--ROM編譯,repo使用指南

1、檢出代碼&#xff0c;指定git指定的分支和manifest repo init -u git://xxx/xxx/manifest.git -b <BRANCH> -m <MANIFEST> 這里-m和-b的含義是&#xff1a; 1. 注意到manifest.git本身也是一個git project 2. 所以&#xff0c;-b指定的是使用#1中這個git proj…

我的相關獎項

博士錄取證明 名單第53&#xff1a;https://yzb.bupt.edu.cn/content/content.php?p2_2_651 論文鏈接 第一篇&#xff1a;https://doi.org/10.1186/s13677-022-00373-8 第二篇&#xff1a;https://doi.org/10.1016/j.ipm.2022.103167 第三篇&#xff1a;https://doi.org/10…

Unity 腳本-生命周期常用函數

在Unity中&#xff0c;萬物皆是由組件構成的。 右鍵創建C&#xff03;腳本&#xff0c;拖動腳本到某物體的組件列表。 生命周期相關函數 using System.Collections; using System.Collections.Generic; using UnityEngine;// 必須要繼承 MonoBehaviour 才是一個組件 // 類名…

matplotlib——散點圖和條形圖(python)

散點圖 需求 我們獲得北京2016年三月和十月每天白天最高氣溫&#xff0c;我們現在需要找出氣溫隨時間變化的某種規律。 代碼 # 導入庫 from matplotlib import pyplot as plt import random# 解決中文亂碼 import matplotlib matplotlib.rc("font",family"F…

【筆記】-編程語言以及應用領域

C/C 永遠不會衰敗的語言&#xff0c;適合偏底層&#xff0c;例如&#xff1a;Windows操作系統80%以上都是由C/C完成的&#xff0c;C/C也集成用于寫應用層C/S架構的軟件 JAVA 是真正的跨平臺的語言 “一次編程&#xff0c;到處使用”Java適合應用層的開發&#xff0c;無論是…

Python的錯誤和異常

文章目錄 python的語法錯誤異常異常處理用戶自定義異常定義清理行為預定義的清理行為 python的語法錯誤 語法錯誤&#xff08;Syntax Error&#xff09;是指代碼不符合Python語言的語法規則。當解釋器在執行代碼之前對其進行解析時&#xff0c;如果發現代碼中有語法錯誤&#…

領域驅動設計(Domain-Driven Design DDD)——戰略設計1

一、概述 隨著系統的增長&#xff0c;它會越來越復雜&#xff0c;當我們無法通過分析對象來理解系統的時候&#xff0c;就需要掌握一些操縱和理解大模型的技術了。 最負雄心的企業欲實現一個涵蓋所有業務、緊密集成的系統。因大型公司的業務模型巨大且復雜&#xff0c;很難把它…

2403d,d的108版本更改

原文 編譯器更改 1,在object.d中給TypeInfo_Class添加了.nameSig字段 2,總是在調用點求值像__FILE__此類關鍵字 3,現在可按整數數組轉換十六進制串 4,添加支持插值式序列 庫更改 1,isForwardRange,isBidirectionalRange和isRandomAccessRange現在帶可選元素類型 2,std.uni已…

K8s 鏡像緩存管理 kube-fledged 認知

寫在前面 博文內容為K8s 鏡像緩存管理 kube-fledged 認知內容涉及&#xff1a; kube-fledged 簡單介紹部署以及基本使用 理解不足小伙伴幫忙指正 不必太糾結于當下&#xff0c;也不必太憂慮未來&#xff0c;當你經歷過一些事情的時候&#xff0c;眼前的風景已經和從前不一樣了。…

springboot236基于springboot在線課程管理系統的設計與實現

基于SpringBoot在線課程管理系統的設計與實現 摘要 本文首先介紹了在線課程管理系統的現狀及開發背景&#xff0c;然后論述了系統的設計目標、系統需求、總體設計方案以及系統的詳細設計和實現&#xff0c;最后對在線課程管理系統進行了系統檢測并提出了還需要改進的問題。本系…

Spring Data Redis 使用方式

在Java中操作Redis 1. 在Java中操作Redis1.1 Redis的Java客戶端1.2 Spring Data Redis使用方式1.2.1 介紹1.2.1 環境搭建1.2.3 操作常見類型數據 1. 在Java中操作Redis 1.1 Redis的Java客戶端 Redis 的 Java 客戶端很多&#xff0c;常用的幾種&#xff1a; JedisLettuceSpri…

關于Axios接口請求超時處理與重試的方法教程

在前端開發中&#xff0c;使用Axios作為HTTP客戶端庫進行接口請求是非常常見的做法。然而&#xff0c;在實際開發中&#xff0c;我們經常會遇到網絡不穩定或服務器響應緩慢導致接口請求超時的情況。為了提高用戶體驗和程序的穩定性&#xff0c;我們需要實現接口請求超時的處理與…

UnityAPI的學習——Transform類

Transform類繼承自Component類&#xff0c;并實現了IEnumberable接口。Transform是GameObject必須擁有得一個組件&#xff0c;用來管理所在GameObject對象的坐標位置、選擇角度、和大小縮放。 Transform實現了IEnumberable接口&#xff0c;因此可以在程序中使用foreach()方法快…

echarts vue 動畫效果的水球圖、波浪圖教程

1、安裝插件 前提是已經安裝了echarts&#xff08;我的版本是4.2.1&#xff09; npm install echarts-liquidfill --save 我安裝了3.1.0版本的&#xff0c;結果運行時報錯"TypeError: wave.ensureState is not a function" 原因&#xff1a;echarts版本和echarts-l…