C++函數模板和類模板

C++另一種編程思想稱為泛型編程,主要利用的技術是模板
C++提供兩種模板機制:函數模板和類模板

C++提供了模板(template)編程的概念。所謂模板,實際上是建立一個通用函數或類,
其類內部的類型和函數的形參類型不具體指定, 用一個虛擬的類型來代表。
這種通用的方式稱為模板。模板是泛型編程的基礎,泛型編程即以一種獨立于任何特定類型的方式編寫代碼。

即:我們提供一個抽象的函數或類,并不具體指定其中數據的類型,而是某個虛擬類型代替。只提供基本的功能。其具體的數據類型,只在其被調用時視具體情況實例化。

函數模板

舉個例子

#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2>             //模板函數聲明與定義
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(10, 5)=" << test(10, 5) << endl;     //調用模板函數,模板函數通過傳入的參數自動推導未實例化的類型cout << "test(5,'A')=" << test(5,'A') << endl;cout << "test(10.5, 5.5) =" << test(10.5, 5.5) << endl;system("pause");return 0;
}

在這里插入圖片描述
函數模板的聲明通過關鍵字template與typename 實現。其中,template告知編譯器這是函數模板的聲明,typename用來聲明虛擬類型。比如你要聲明一個模板函數,里面需要兩個不同的變量,那么你就需要通過typename聲明兩個不同的虛擬類型T1,T2。

聲明好后,你就可以在函數定義中使用虛擬類型來定義變量,但是要注意,用同一個虛擬類型定義的變量就只能是同種類型,比如用T1定義的變量只能是同種變量,可以是int,也可以是char。這取決于其實例化時被實例化為哪種類型。

C++函數模板注意事項
注意事項:
1、自動類型推導,必須推導出一致的數據類型T,才可以使用
2、模板必須要確定出T的數據類型,才可以使用

using namespace std;
template<class T>
void mySwap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void test01()
{int a = 10;int b = 20;char c = 'c';//1、自動類型推導,必須推導出一致的數據類型T,才可以使用mySwap(a, b);//mySwap(a, c);推導不出一致的T類型cout << "a = " << a << endl;cout << "b = " << b << endl;
}template<class T>
void func()
{cout << "func()的調用" << endl;
}void test02()
{//2、模板必須要確定出T的數據類型,才可以使用func<int>();
}int main() {test01();test02();return 0;
}

模板函數的調用
1)顯式類型調用
可以顯式的調用模板函數,即在調用時人為地指明虛擬類型的具體類型。

#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2>             //模板函數聲明與定義
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(5,'A')=" << test<int,char>(5, 'A') << endl;          //<int,char>顯式的指明模板的類型system("pause");return 0;
}

2)自動推導
即不指明具體的數據類型,而讓編譯器根據傳入的數據自動推導出數據類型。

#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2>             //模板函數聲明與定義
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(5,'A')=" << test(5, 'A') << endl;          //自動推導數據類型system("pause");return 0;
}

模板函數與函數重載

熟悉函數重載的人應該會好奇,如果既有模板函數又有同名的普通函數,而且參數列表的參數個數是一樣的,那么在主函數中調用同名函數,編譯器具體會調用哪一個呢?

下面看一個例子:

#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2>             //模板函數聲明與定義
T1 test(T1 tmp, T2 tmp1) {cout << "調用模板函數!" << endl;return (tmp + tmp1);
}int test(int tmp, int tmp1) {                  //重載的普通函數cout << "調用普通函數!" << endl;return 0;
}int main(void) {char tmp = 'c';int tmp1 = 0;int a = 5;cout << "test(5,'c')=" << test(a, tmp) << endl;     cout << "test(5,0)=" << test(a, tmp1) << endl;system("pause");return 0;
}

在這里插入圖片描述
普通函數的兩個參數都是int型,在第一次調用test時第二個參數使用的是char型,調用的是模板函數,第二次使用的是int型,調用的是普通函數。

這是為什么呢?理論上來說,模板函數兩個都能匹配,使用。而普通函數也能匹配這兩次調用的參數(在C語言中,char型變量是可以作為int型參數使用的)。

這是因為模板函數可以自動推導類型,在第一次調用中,兩個類型分別被推導為int型與char型。而普通函數是兩個int型,雖然也能使用傳入的參數,但模板函數明顯能更好的匹配參數列表。

也就是說,如果模板函數實例化后的類型能更好的匹配參數列表的話就使用模板函數。

那么當這兩個函數都能完全匹配參數列表的時候呢?通過第二次test的調用結果不難發現,這時候,編譯器會調用普通函數。

如果一定要使用模板函數,可以使用<>顯式的指定使用模板函數。看下面的例子。

#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2>             //模板函數聲明與定義
T1 test(T1 tmp, T2 tmp1) {cout << "調用模板函數!" << endl;return (tmp + tmp1);
}int test(int tmp, int tmp1) {                  //重載的普通函數cout << "調用普通函數!" << endl;return 0;
}int main(void) {char tmp = 'c';int tmp1 = 0;int a = 5;cout << "test(5,'A')=" << test(a, tmp) << endl;     cout << "test<>(5,0)=" << test<>(a, tmp1) << endl;       //使用<>顯式的調用模板函數system("pause");return 0;
}

在這里插入圖片描述

類模板

類模板是為了減少重復工作量而出現的一種進制,當一個類的功能類似只是類型不同時,一個通用的類模板可以根據使用者的需要而生成具體類型的類,從而減少功能重復的代碼。

類模板作用:建立一個通用類,類中的成員 數據類型可以不具體制定,用一個虛擬的類型來代表
解釋:
template–聲明創建模板
typename–表明其后面的符號是一種數據類型,可以用class代替
T–通用的數據類型,名稱可以替換,通常為大寫字母

在類內部定義與聲明

#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class DEMO {public:DEMO(T data) {this->data = data;}~DEMO() {}int GetData() {return this->data;}private:T data;
};template <typename T,typename T1>
class DEMO1 {public:DEMO1() {}~DEMO1();private:T data;T1 ch;
};int main(void) {DEMO<int> demo(5);           //顯示的指定類型為intDEMO1<int, char> demo1();    //顯示的指定類型分別為int,charcout << "data=" << demo.GetData() << endl;system("pause");return 0;
}

類模板的定義與使用使用的是和模板函數一樣的關鍵字。即先聲明template,在使用typename聲明虛擬類型。

與模板函數不同的是,類模板不能被自動推導出類型,只能顯示的指定具體的類型。如上面代碼中的 DEMO< int > demo(5),該模板類被顯示的指定為int型。

在類外部定義成員函數

在類內部聲明成員函數,在類外部定義成員函數時,只要成員函數參數列表中出現了類限定域說明,模板類作為返回類型,模板類作為參數類型,那么就要在成員函數之前聲明 template <類型形式參數表>,并且在模板類后加上虛擬參數列表。

#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class DEMO {public:DEMO(T data);~DEMO();DEMO operator+(int sum);int PrintData(DEMO& demo);private:T data;
};template<typename T>          //出現了類限定域說明
DEMO<T>::DEMO(T data)
{this->data = data;
}template<typename T>         //出現了類限定域說明
DEMO<T>::~DEMO()
{
}template<typename T>                //出現了作為返回值類型的模板類類型
DEMO<T> DEMO<T>::operator+(int sum)
{return *this;
}template<typename T>               //出現了作為參數類型的模板類類型
int DEMO<T>::PrintData(DEMO<T>& demo)
{cout << "data=" << demo.data << endl;return 0;
}int main(void) {DEMO<int> demo(5), demo1(15);demo.PrintData(demo1);demo + 5;system("pause");return 0;
}

在這里插入圖片描述
DEMO< T >中的< T >是虛擬參數列表。

總結來說,只要看到了模板類的類名關鍵字出現在成員函數參數列表中,就要在成員函數之前聲明 template <類型形式參數表>,并且在模板類類名后加上虛擬參數列表。

模板類的繼承

分為三種情況。一:子類是模板類,父類是普通類;二:父類是模板類,子類是普通類;三:父類與子類都是模板類。其中第一種情況與兩個普通類的繼承是一樣的。就不說了。

1)父類是模板類,子類是普通類:

#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class FATHER {public:FATHER(T data) {this->data = data;}~FATHER() {}private:T data;
};class SON:public FATHER<int> {        //顯示的指明父類的具體類型public:SON(int data):FATHER<int>(data) {this->data = data;}~SON() {}int GetData() {return data;}private:int data;
};int main(void) {SON son(15);cout << "data=" << son.GetData() << endl;system("pause");return 0;
}

子類是一般類,父類是模板類,繼承時必須在子類里實例化父類的類型參數。其實這很好理解,因為在子類對象構造之前,會先調用父類的構造函數,父類為模板類,要想實例化,需要有指定的類型。

2)父類與子類都是模板類:

#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class FATHER {public:FATHER(T data) {this->data = data;}~FATHER() {}private:T data;
};template <typename T1>
class SON:public FATHER<T1> {       //使用子類的模板類型傳遞到父類中,也可以使用具體的類型public:SON(int data):FATHER<int>(data) {this->data = data;}~SON() {}int GetData() {return data;}private:T1 data;
};int main(void) {SON<int> son(15);cout << "data=" << son.GetData() << endl;system("pause");return 0;
}

當子類與父類都是模板類時,繼承時也必須在子類里實例化父類的類型參數,值得注意的是,此時實例化的類型參數可以使用子類的模板類型。即讓父類與子類在實例化后擁有一樣的具體類型。當然也可以使用其它的具體類型。

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

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

相關文章

Axios使用CancelToken取消重復請求

處理重復請求&#xff1a;沒有響應完成的請求&#xff0c;再去請求一個相同的請求&#xff0c;會把之前的請求取消掉 新增一個cancelRequest.js文件 import axios from "axios" const cancelTokens {}export const addPending (config) > {const requestKey …

如何區分閏年與平年

首先要明白 地球繞太陽運行周期為365天5小時48分46秒&#xff08;合365.24219天&#xff09;&#xff0c;即一回歸年&#xff08;tropical year&#xff09;。公歷的平年只有365日&#xff0c;比回歸年短約0.2422 日&#xff0c;每四年累積約一天&#xff0c;把這一天加于2月末…

Docker安裝基礎使用練習

目錄 1、安裝Docker-CE 1&#xff09;簡單使用yum方式安裝 ! 2&#xff09;配置鏡像加速&#xff1a; 2、下載系統鏡像&#xff08;Ubuntu、 centos&#xff09; 1&#xff09;先查看我們所需的鏡像有哪些版本。使用search命令&#xff01; 2&#xff09;下載鏡像使用的是pul…

【爬蟲】P1 對目標網站的背景調研(robot.txt,advanced_search,builtwith,whois)

對目標網站的背景調研 檢查 robot.txt估算網站大小識別網站所用技術尋找網站的所有者 檢查 robot.txt 目的&#xff1a; 大多數的網站都會包含 robot.txt 文件。該文件用于指出使用爬蟲爬取網站時有哪些限制。而我們通過讀 robot.txt 文件&#xff0c;亦可以最小化爬蟲被封禁的…

vue中實現文字檢索時候將搜索內容標紅

實現結果 html&#xff1a; <div class"searchBox"><span class"bt">標&#8195&#8195題</span><div class"search"><div class"shuru"><!-- <span class"title">生產經營<…

[leetcode] 707 設計鏈表

707. 設L計鏈表 中等 902 相關企業 你可以選擇使用單鏈表或者雙鏈表&#xff0c;設計并實現自己的鏈表。 單鏈表中的節點應該具備兩個屬性&#xff1a;val 和 next 。val 是當前節點的值&#xff0c;next 是指向下一個節點的指針/引用。 如果是雙向鏈表&#xff0c;則還需…

如何批量修改圖片名為不同名稱

如何批量修改圖片名為不同名稱&#xff1f;當今社會&#xff0c;因為人們都養成了隨手拍照的習慣&#xff0c;所以擁有上千上萬張照片的相冊已經司空見慣不足為奇。然而&#xff0c;我們在保存這些照片時往往都會碰到一個大難題——電腦中的圖片名稱千奇百怪&#xff0c;讓整個…

C++并發多線程--std::async、std::packaged_task和std::promise的使用

目錄 1--std::async的使用 2--std::packaged_task的使用 3--std::promise的使用 1--std::async的使用 std::async用于啟動一個異步任務&#xff0c;并返回一個std::future對象&#xff1b;std::future對象里含有異步任務線程入口函數的結果&#xff1b; std::launch::deferr…

完美解決微信小程序使用復選框van-checkbox無法選中

由于小程序使用了vant-ui框架&#xff0c;導致checkbox點擊無法選中問題 <van-checkbox value"{{ checked }}" shape"square"><view class"check-content"><view class"checktext">我已閱讀并同意>《用戶協議》…

opencv-目標追蹤

import argparse import time import cv2 import numpy as np# 配置參數 ap argparse.ArgumentParser() ap.add_argument("-v", "--video", typestr,help"path to input video file") ap.add_argument("-t", "--tracker", …

第1天----驗證一個字符串是否是另一個字符串的子串

本文我們將學習如何去驗證一個字符串是否是另一個字符串的子串。 一、小試牛刀&#xff1a; 題目描述 輸入兩個字符串&#xff0c;驗證其中一個串是否為另一個串的子串。 輸入格式 兩行&#xff0c;每行一個字符串。 輸出格式 若第一個串 s 1 是第二個串 s 2 的子串&#xff0c…

java Spring Boot properties多環境配置拆分文件管理

上文 java Spring Boot yml多環境拆分文件管理優化 我們用yml 做了一個多環境配置文件的拆分管理 我們將 application.yml 改為 application.properties 參考代碼如下 spring.profiles.activedev我們知道 yml 是用 : 來區分高低基本 而 properties是直接通過 . 來表達 其他基本…

使用svd 分解的方法對神經網絡模型進行壓縮(能不能壓縮要看秩的大小)

參考和理論 壓縮原理代碼 import torch import numpy as np torch.manual_seed(0)# ------------------------------------ # n:輸入數據維度 # m:輸出數據維度 # ------------------------------------ n = 12 m = 10# ------------------------------------ # 隨機初始化權…

樹形組件淺知

別人寫好的輪子&#xff0c;會用即可 首先&#xff0c;需要安裝依賴&#xff0c;npm install --save riophae/vue-treeselect 如果使用npm 不行 那么就使用 yarn add --save riophae/vue-treeselect 然后在使用的地方引入即可 // import the componentimport Treeselect from…

微信ipad協議8.0.40 加好友功能

友情鏈接 geweapi.com 點擊即可訪問&#xff01; 好友請求驗證 小提示&#xff1a; v_3 v_4 可以參考 搜索接口 請求URL&#xff1a; http://域名地址/api/contacts/verifyuser 請求方式&#xff1a; POST 請求頭&#xff1a; Content-Type&#xff1a;application/js…

SpringCloud實用篇7——深入elasticsearch

目錄 1 數據聚合1.1 聚合的種類1.2 DSL實現聚合1.2.1 Bucket聚合語法1.2.2 聚合結果排序1.2.3 限定聚合范圍1.2.4 Metric聚合語法1.2.5.小結 1.3 RestAPI實現聚合1.3.1 API語法1.3.2 業務需求1.3.3 業務實現 2 自動補全2.1 拼音分詞器2.2 自定義分詞器2.3 自動補全查詢2.4 實現…

POJ 1995 Raising Modulo Numbers 快速冪

一、總結 我一開始擔心溢出&#xff0c;開了一個無符號的long long&#xff0c;但是直接超時&#xff0c;后來一看它的mod不是很大&#xff0c;于是改成int&#xff0c;直接過了。 二、代碼 #include <iostream> using namespace std; int H, Z; int M; int mulMod(in…

P1217 [USACO1.5] 回文質數 Prime Palindromes

P1217 [USACO1.5] 回文質數 Prime Palindromes - 洛谷 | 計算機科學教育新生態 (luogu.com.cn) # [USACO1.5] 回文質數 Prime Palindromes ## 題目描述 因為 $151$ 既是一個質數又是一個回文數&#xff08;從左到右和從右到左是看一樣的&#xff09;&#xff0c;所以 $151$ …

Python基礎教程:私有變量的訪問和賦值教程

嗨嘍~大家好呀&#xff0c;這里是魔王吶 ? ~! 首先我們這里先描述下&#xff1a; Python中&#xff0c;變量名類似__x__的&#xff0c;以雙下劃線開頭&#xff0c;并且以雙下劃線結尾的&#xff0c;是特殊變量&#xff0c;特殊變量是可以直接訪問的&#xff08;比如 doc, __i…

SpringBoot3集成ElasticSearch

標簽&#xff1a;ElasticSearch8.Kibana8&#xff1b; 一、簡介 Elasticsearch是一個分布式、RESTful風格的搜索和數據分析引擎&#xff0c;適用于各種數據類型&#xff0c;數字、文本、地理位置、結構化數據、非結構化數據&#xff1b; 在實際的工作中&#xff0c;歷經過Ela…