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

就比如非常常用的swap函數,雖然c++支持重載,但是如果對于這種函數我們每一個都要自己去實現,是一件很麻煩并且多余的工作,因為我們將重復邏輯的東西多次實現。

  • 比如我們想向一個封裝好的類添加新的數據成員,重新寫一個不如一開始就寫成泛型。
class Stack
{private:int capacity;int size;int* arr;
};

如果我們實現了一個棧,如果他此時要存儲其他的數據類型,那我們就必須還要重新修改一個,這無疑是繁瑣的。

所以,C++ 基于重載這一項機制,實現了模板編程,模板作為類或者函數的藍圖,可以針對不同類型,來實現對應操作。


函數模板

函數模板與類型無關,被調用時根據實參類型產生函數的對應類型版本。

template<typename T1, typename T2,......,typename Tn>
//typename即類型名,也可以用class替代,這里的class指的是類型,不能用struct替換

編寫函數模板時,只需要在對應的函數前面加上上面的語句即可,然后將所有需要替換類型的參數改為上面的 T1、T2 即可

template<class	T>
void Swap(T& left, T& right) {T temp = left;left = right;right = temp;
}int main()
{int i = 3, j = 4;double a = 3.4, b = 5.6;char x = 'x', y = 'y';Swap(i, j);Swap(a, b);Swap(x, y);cout << i << ' ' << j << endl;cout << a << ' ' << b << endl;cout << x << ' ' << y << endl;
}

在這里插入圖片描述

當我們調用這個模板函數的時候,是生成一個函數重載并調用三次呢?還是生成三個返回值不同的函數各調用一次呢?

在這里插入圖片描述
通過地址信息可以看到,它調用的是三個不同的函數。

因此,模板函數本身并不是一個函數,而是一個藍圖,通過識別我們傳入的參數,然后在底層生成一個該類型的模板函數,并調用該函數。

并且這個階段是在預處理的時候就進行了,因為如果它是在編譯階段才進行函數的生成,那肯定是無法通過語法的檢查的,所以這一階段只能在預處理進行。


函數模板實例化

函數模板只在第一次使用的時候才會實例化出來,它的實例化有兩種方法,一種是顯式實例化,一種是隱式實例化。

隱式實例化

template<class T> T Add(const T& left, const T& right) 
{return left + right;
}int main()
{int i = 3, j = 4;double a = 3.4, b = 5.6;Add(i, j);Add(a, b);/*Add(i, a);這時編譯器就會報錯,因為需要通過參數推演出對應類型,但是此時就無法推演出到底是將 T 推演成 double 還是將 T 推演成 int*/// 此時有兩種處理方式:1. 用戶自己來強制轉化 2. 使用顯式實例化Add(i, (int)a); // 強制轉化,將 T 推演成 intreturn 0;
}

顯式實例化

int main() {int i = 3;double x = 3.4;// 顯式實例化Add<int>(i, x);return 0; }

用尖括號來顯式的聲明,STL 中的容器等就是采用這種顯式的實例化來明確參數的類型。


函數模板的匹配規則

  1. 一個非模板函數可以和一個同名的函數模板同時存在,而且該函數模板還可以被實例化為這個非模板函數。這時如果使用隱式實例化,則會調用非模板函數;如果使用顯式實例化,則調用模板函數
//非模板函數
int Add(int left, int right) 
{return left + right;
}template<class T> 
T Add(T left, T right) 
{return left + right;
}int main()
{Add(1, 2); // 調用非模板函數Add<int>(1, 2); // 調用模板函數
}
  1. 對于非模板函數和同名函數模板,如果其他條件都相同,在調動時會優先調用非模板函數而不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數, 那么將選擇模板函數
// 專門處理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函數
}
  1. 模板函數不允許自動類型轉換,但普通函數可以進行自動類型轉換。

類模板

類模板的用法和函數模板其實一樣

template<class T>
class Stack
{private:int capacity;int size;T* arr;
};

這里的 Stack 不是一個具體的類,他是類模板,也就是一個藍圖,通過這個模板來識別參數的類型并生成對應的模板類。

所以可以理解為類模板是一個類家族模板類是通過類模板實例化的具體類

如果類中的成員函數需要在類外定義的話,需要每一個定義前都要聲明一次類模板的參數列表

template<class T>
class Stack
{
public:void push();void pop();
private:int capacity;int size;T* arr;
};template<class T>
void Stack<T>::push()
{}template<class T>
void Stack<T>::pop()
{}

還有一個需要注意的地方就是,類模板的所有成員函數都是模板函數

類模板的實例化

類模板只能夠通過顯式實例化

STL 中的幾個容器都是通過類模板實現的:

	stack<int> s1;stack<double> s2;

stack<int> 并不是類名,而是顯式實例化指定的類型,stack 才是類名。

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

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

相關文章

你真的了解靜態變量、常量的存儲位置嗎?

文章目錄引言C對內存的劃分如何落實在Linux上自由存儲區和堆之間的問題棧常量區靜態存儲區靜態局部變量靜態局部變量、靜態全局變量、全局變量的異同macOS系統的測試結果總結引言 在動態內存的博客中&#xff0c;我提到&#xff1a; 在Linux 內存管理的博客中&#xff0c;我提…

C++ 泛型編程(二):非類型模板參數,模板特化,模板的分離編譯

文章目錄非類型模板參數函數模板的特化類模板的特化全特化偏特化部分參數特化參數修飾特化模板分離編譯解決方法非類型模板參數 模板的參數分為兩種&#xff1a; 類型參數&#xff1a; 則是我們通常使用的方式&#xff0c;就是在模板的參數列表中在 class 后面加上參數的類型…

Java操作——獲取文件擴展名,去掉文件擴展名

昨天收郵件&#xff0c;得知要參加一個產品部的會議&#xff0c;猜想&#xff0c;也許是因為我做的這個產品demo問題。于是昨天忙活到凌晨3點半&#xff0c;結果早上一來才知道又被調戲了。發郵件的MM把郵件誤發給我了。悲催啊有木有&#xff0c;困啊有木有&#xff01;自己還是…

數據結構 | B樹、B+樹、B*樹

文章目錄搜索結構B樹B樹的插入B樹的遍歷B樹的性能B樹B樹的插入B樹的遍歷B*樹B*樹的插入總結搜索結構 如果我們有大量的數據需要永久存儲&#xff0c;就需要存儲到硬盤之中。但是硬盤的訪問速度遠遠小于內存&#xff0c;并且由于數據量過大&#xff0c;無法一次性加載到內存中。…

MySQL 索引 :哈希索引、B+樹索引、全文索引

文章目錄索引引言常見的索引哈希索引自適應哈希索引B樹索引聚集索引非聚集索引使用方法聯合索引最左前綴匹配規則覆蓋索引全文索引使用方法索引 引言 為什么需要索引&#xff1f; 倘若不使用索引&#xff0c;查找數據時&#xff0c;MySQL必須遍歷整個表。而表越大&#xff0c;…

服裝店怎么引流和吸引顧客 服裝店鋪收銀系統來配合

實體店的同城引流和經營是實體經濟的一個重要的一環&#xff0c;今天我們來分享服裝行業的實體店鋪怎么引流和吸引、留住顧客&#xff0c;并實現復購。大家點個收藏&#xff0c;不然劃走就再也找不到了&#xff0c;另外可以點個關注&#xff0c;下次有新的更好的招&#xff0c;…

約瑟夫環(丟手絹問題)

文章目錄問題描述思路代碼實現問題描述 有 1~N 個數字&#xff0c;從 1~m 依次報數&#xff0c;數到 m 的數字要被刪掉&#xff0c;求最后剩下的數字是&#xff1f; 思路 第一次報數第二次報數1n-m12n-m2……m-2n-2m-1n-1m被刪掉了m11m22……n-1n-1-mnn-m 通過上面的表格&…

MySQL 鎖的相關知識 | lock與latch、鎖的類型、簡談MVCC、鎖算法、死鎖、鎖升級

文章目錄lock與latch鎖的類型MVCC一致性非鎖定讀&#xff08;快照讀&#xff09;一致性鎖定讀&#xff08;當前讀&#xff09;鎖算法死鎖鎖升級lock與latch 在了解數據庫鎖之前&#xff0c;首先就要區分開 lock 和 latch。在數據庫中&#xff0c;lock 和 latch 雖然都是鎖&…

Hibernate使用原生SQL適應復雜數據查詢

HQL盡管容易使用&#xff0c;但是在一些復雜的數據操作上功能有限。特別是在實現復雜的報表統計與計算&#xff0c;以及多表連接查詢上往往無能為力&#xff0c;這時可以使用SQL&#xff08;Native SQL&#xff09;實現HQL無法完成的任務。 1、使用SQL查詢 使用SQL查詢可以通過…

MySQL 存儲引擎 | MyISAM 與 InnoDB

文章目錄概念innodb引擎的4大特性索引結構InnoDBMyISAM區別表級鎖和行級鎖概念 MyISAM 是 MySQL 的默認數據庫引擎&#xff08;5.5版之前&#xff09;&#xff0c;但因為不支持事務處理而被 InnoDB 替代。 然而事物都是有兩面性的&#xff0c;InnoDB 支持事務處理也會帶來一些…

MySQL 事務 | ACID、四種隔離級別、并發帶來的隔離問題、事務的使用與實現

文章目錄事務ACID并發帶來的隔離問題幻讀&#xff08;虛讀&#xff09;不可重復讀臟讀丟失更新隔離級別Read Uncommitted (讀未提交)Read Committed (讀已提交)Repeatable Read (可重復讀)Serializable (可串行化)事務的使用事務的實現Redoundo事務 事務指邏輯上的一組操作。 …

MySQL 備份與主從復制

文章目錄備份主從復制主從復制的作用備份 根據備份方法的不同&#xff0c;備份可劃分為以下幾種類型&#xff1a; 熱備(Hot Backup) &#xff1a; 熱備指的是在數據庫運行的時候直接備份&#xff0c;并且對正在運行的數據庫毫無影響&#xff0c;這種方法在 MySQL 官方手冊中又…

C++ 流的操作 | 初識IO類、文件流、string流的使用

文章目錄前言IO頭文件iostreamfstreamsstream流的使用不能拷貝或對 IO對象 賦值條件狀態與 iostate 類型輸出緩沖區文件流fstream類型文件模式文件光標函數tellg() / tellp()seekg() / seekp()向文件存儲內容/讀取文件內容string流istringstreamostringstream前言 我們在使用 …

Hibernate 更新部分更改的字段 hibernate update

Hibernate 中如果直接使用 Session.update(Object o);或則是Session.updateOrUpdate(Object o); 會把這個表中的所有字段更新一遍。 如&#xff1a; ExperClass4k e new ExperClass4k(); e.setTime(time); e.setQ_num(q_num); e.setK(k); if (str "finch_fix")…

C++ 類的行為 | 行為像值的類、行為像指針的類、swap函數處理自賦值

文章目錄概念行為像值的類行為像指針的類概念引用計數動態內存實現計數器類的swap概念swap實現自賦值概念 行為像值的類和行為像指針的類這兩種說法其實蠻拗口的&#xff0c;這也算是 《CPrimer》 翻譯的缺點之一吧。。。 其實兩者的意思分別是&#xff1a; 行為像值的類&am…

C++ 右值引用 | 左值、右值、move、移動語義、引用限定符

文章目錄C11為什么引入右值&#xff1f;區分左值引用、右值引用move移動語義移動構造函數移動賦值運算符合成的移動操作小結引用限定符規定this是左值or右值引用限定符與重載C11為什么引入右值&#xff1f; C11引入了一個擴展內存的方法——移動而非拷貝&#xff0c;移動較之拷…

且談關于最近軟件測試的面試

前段時間有新的產品需要招人&#xff0c;安排和參加了好幾次面試&#xff0c;下面就談談具體的面試問題&#xff0c;在面試他人的同時也面試自己。 面試問題是參與面試同事各自設計的&#xff0c;我也不清楚其他同事的題目&#xff0c;就談談自己設計的其中2道題。 過去面試總是…

C++ 多態 | 虛函數、抽象類、虛函數表

文章目錄多態虛函數重寫重定義&#xff08;參數不同&#xff09;協變&#xff08;返回值不同&#xff09;析構函數重寫&#xff08;函數名不同&#xff09;final和override重載、重寫、重定義抽象類多態的原理虛函數常見問題解析虛函數表多態 一種事物&#xff0c;多種形態。換…

C++ 運算符重載(一) | 輸入/輸出,相等/不等,復合賦值,下標,自增/自減,成員訪問運算符

文章目錄輸出運算符<<輸入運算符>>相等/不等運算符復合賦值運算符下標運算符自增/自減運算符成員訪問運算符輸出運算符<< 通常情況下&#xff0c;輸出運算符的第一個形參是一個 非常量ostream對象的引用 。之所以 ostream 是非常量是因為向流寫入內容會改變…

C++ 重載函數調用運算符 | 再探lambda,函數對象,可調用對象

文章目錄重載函數調用運算符lambdalambda等價于函數對象lambda等價于類標準庫函數對象可調用對象與function可調用對象function函數重載與function重載函數調用運算符 函數調用運算符必須是成員函數。 一個類可以定義多個不同版本的調用運算符&#xff0c;互相之間應該在參數數…