C++模板剖析:函式模板、類模板解析
C++中關於模板&泛型程式設計問題:
問題引入:何編寫一個通用加法函式?
(1)使用函式過載,針對每個所需相同行為的不同型別重新實現它
int Add(const int &_iLeft, const int&_iRight)
{
return (_iLeft +_iRight);
}
float Add(const float &_fLeft, constfloat &_fRight)
{
return (_fLeft +_fRight);
}
【缺點】
1、只要有新型別出現,就要重新新增對應函式。
2、除型別外,所有函式的函式體都相同,程式碼的複用率不高
3、如果函式只是返回值型別不同,函式過載不能解決
4、一個方法有問題,所有的方法都有問題,不好維護。
(2)使用公共基類,將通用的程式碼放在公共的基礎類裡面
【缺點】
1、藉助公共基類來編寫通用程式碼,將失去型別檢查的優點;
2、對於以後實現的許多類,都必須繼承自某個特定的基類,程式碼維護更加困難。
(3)使用特殊的預處理程式
#define ADD(a, b) ((a) + (b))
【缺點】
不是函式,不進行引數型別檢測,安全性不高
綜上所述的問題,我們需要引入泛型程式設計,即為需要的函式或者類編寫一個模板,在實用的時候例項化即可。那麼,什麼是泛型程式設計?什麼是模板?
一、 泛型程式設計
泛型程式設計:編寫與型別無關的邏輯程式碼,是程式碼複用的一種手段。模板是泛型程式設計的基礎。
二、 函式模板
函式模板:代表了一個函式家族,該函式與型別無關,在使用時被引數化,根據實參型別產生函式的特定型別版本。
模板函式定義的格式:template<typename T1, teypename T2, ……….typename Tn>
函式返回值 函式名(引數列表)
{
. . . . . .
}
Eg:
template<typename T>
T Add( T left, T right )
{
return left+right;
}
template和typename 為關鍵字,T為模板形參的名字,可隨意命名。
typename是用來定義模板引數關鍵字,也可以使用class。建議儘量使typename。
例項化:模板是一個藍圖,它本身不是類或者函式,編譯器用模板產生指定的類或者函式的特定型別版本,產生模板特定型別的過程稱為函式模板例項化
注:模板被編譯了兩次:
① 例項化之前,檢查模板程式碼本身,檢視是否出現語法錯誤,如:遺漏分號
②在例項化期間,檢查模板程式碼,檢視是否所有的呼叫都有效,如:例項化型別不支援某些函式呼叫
實參推演:
從函式實參確定模板形參型別和值的過程稱為模板實參推斷,多個型別形參的實參必須完全匹配
型別形參轉換:
一般不會轉換實參以匹配已有的例項化,相反會產生新的例項。
編譯器只會執行兩種轉換:
1、const轉換:接收const引用或者const指標的函式可以分別用非const物件的引用或者指標來呼叫
2、陣列或函式到指標的轉換:如果模板形參不是引用型別,則對陣列或函式型別的實參應用常規指標轉換。陣列實參將當做指向其第一個元素的指標,函式實參當做指向函式型別的指標。
Eg:
template<typename T>
void FunTest1(const T* t)
{
cout<<"FunTest1();"<<*t<<endl;
}
template<typename T>
void FunTest2(const T& t)
{
cout<<"FunTest2();"<<t<<endl;
}
template<typename T>
void FunTest3(T t1, T t2)
{
cout<<"FunTest3()"<<endl;
}
int Add(int a, int b)
{
return a+b;
}
int main()
{
int A = 10;
int* pA = &A;
FunTest1(pA);
int b = 20;
int& pB = b;
FunTest2(pB);
int array1[10];
int array2[20];
FunTest3(array1, array2);
FunTest3(Add,Add);
system("pause");
return 0;
}
模板引數:函式模板有兩種型別引數:模板引數和呼叫引數。
(1) 模板形參:
a、模板形參名字只能在模板形參之後到模板宣告或定義的末尾之間使用,遵循名字遮蔽規則。
b、模板形參的名字在同一模板形參列表中只能使用一次。
Template<typename T, typenameT>
Void FunTest(T t1, T t2)
{};
c、 所有模板形參前面必須加上class或者typename關鍵字修飾,並且兩個關鍵字可以混用。
d、在函式模板的內部不能指定預設的模板實參。
Template<typename T>
T Add(T = int , T) //編譯出錯。
{};
非模板型別引數:
非模板型別形參是模板內部定義的常量,在需要常量表達式的時候,可以使用非模板型別引數。
注:
1、模板形參表使用<>括起來。
2、和函式引數表一樣,跟多個引數時必須用逗號隔開,型別可以相同也可以不相同。
template<typenameT, typename V …….>
3、模板形參表不能為空(模板特化的時候,模板引數為空)。
4、模板形參可以是型別形參,也可以是非型別新參,型別形參跟在class和typename後
5、模板型別形參可作為型別說明符用在模板中的任何地方,與內建型別或自定義型別使用方法完全相同,可用於指定函式形參型別、返回值、區域性變數和強制型別轉換。
6、模板形參表中,class和typename具有相同的含義,可以互換,使用typename更加直觀。但關鍵字typename是作為C++標準加入到C++中的,舊的編譯器可能不支援。
模板函式過載:
注:
1、函式的所有過載版本的宣告都應該位於該函式被呼叫位置之前
2、一個非模板函式可以和一個同名的函式模板同時存在,而且該函式模板還可以被例項化為這個非模板函式。
3、對於非模板函式和同名函式模板,如果其他條件都相同,在調動時會優先調動非模板函式而不會從該模板產生出一個例項。如果模板可以產生一個具有更好匹配的函式,那麼將選擇模板。
4、顯式指定一個空的模板實參列表,該語法告訴編譯器只有模板才能來匹配這個呼叫,而且所有的模板引數都應該根據實參演繹出來。
5、模板函式不允許自動型別轉換,但普通函式可以進行自動型別轉換。
新的問題引入:
string s1 = "addfhgj";
string s2 = "addfghjkl";
Max(s1,s2); //未能從“const std::string”為“const std::move_iterator<_RanIt> &”推導 模板 引數。
故模板有一些特殊的情況不能處理,就需要引入模板的特化,什麼是模板的特化?
模板函式特化:
有時候並不總是能夠寫出對所有可能被例項化的型別都最合適的模板,在某些情況下,通用模板定義對於某個型別可能是完全錯誤的,或者不能編譯,或者做一些錯誤的事情。
Eg:比較兩個字串的大小
除錯之後發現比較的是字串地址的大小而不是字串的大小。因此需要對模板函式進行特化以處理特殊的情況。這就需要對模板類的特殊情況進行處理-------模板特化:
注意:
在模板特化版本的呼叫中,實參型別必須與特化版本函式的形參型別完全匹配,如果不匹配,編譯器將為實參模板定義中例項化一個例項。
特化不能出現在模板例項的呼叫之後,應該在標頭檔案中包含模板特化的宣告,然後使用該特化版本的每個原始檔包含該標頭檔案。
三、 模板類
1、 模板類的定義格式:
template<class 形參名1, class 形參名2, ...class 形參名n>
class 類名
{ ... };
// 使用模板方式實現動態順序表
template<typename T>
class SeqList
{
public :
SeqList();
~ SeqList();
private :
int _size ;
int _capacity ;
T* _data ;
};
template <typename T>
SeqList <T>:: SeqList()
: _size(0)
, _capacity(10)
, _data(new T[ _capacity])
{}
template <typename T>
SeqList <T>::~ SeqList()
{
delete [] _data ;
}
void Test()
{
SeqList<int>s1;
SeqList<double>s2;
SeqList<char>s3;
}
2、 模板類的例項化
只要有一種不同的型別,編譯器就會例項化出一個對應的類。
SeqList<int > sl1;
SeqList<double > sl2;
當定義上述兩種型別的順序表時,編譯器會使用int和double分別代替模板形參,重新編寫SeqList類,最後建立名為SeqList<int>和SeqList<double>的類。
(1)模板引數實現容器介面卡。
#include "List.h"
//底層使用List 容器
template <class T, class container = list<T>> //模板引數
class Queue
{
public:
void push(const T& x)
{
return _con.PushBack(x);
}
void pop()
{
return _con.PopFront();
}
const T& GetHead()
{
return _con.Front();
}
const T& GetTail()
{
return _con.Back();
}
bool IsEmpty()
{
return _con.Empty();
}
private:
container _con;
};
void Test2()
{
Queue<int> q1;//使用預設的模板引數構造物件
Queue<int, List<int>> q2;//使用模板引數構造物件
}
(2)模板的模板引數實現容器介面卡。
template <class T, template<class> class container = List> //使用模板的模板引數
class Queue
{
public:
void push(const T& x)
{
return _con.PushBack(x);
}
void pop()
{
return _con.Pop();
}
const T& GetHead()
{
return _con.Front();
}
const T& GetTail()
{
return _con.Back();
}
bool IsEmpty()
{
return _con.Empty();
}
private:
container<T> _con;
};
void Test1()
{
Queue<int>q1;//使用預設的模板類的模板引數構造物件
Queue<int,List> q2;//使用模板的模板引數構造不同型別物件
}
(3)非型別的類模板引數
template <typename T, size_t MAX_SIZE = 10>//帶預設模板引數
//template<typename T, double MAX_SIZE = 10.0> // “double”: 非型別模板引數“MAX_SIZE”的型別非法
class Array
{
public :
Array();
private :
T _array [MAX_SIZE];
int _size ;
};
template <typename T, size_t MAX_SIZE>
Array <T,MAX_SIZE>::Array()
: _size(0)
{}
void Test()
{
Array<int> a1;
Array<int , 20> a2;
}
(4)類模板的特化:
//順序表類的部分實現
template <typename T>
class SeqList
{
public :
SeqList();
~ SeqList();
private :
int _size ;
int _capacity ;
T* _data ;
};
template<typename T>
SeqList <T>:: SeqList()
: _size(0)
, _capacity(10)
, _data(new T[ _capacity])
{
cout<<"SeqList<T>:: SeqList()" <<endl;
}
template<typename T>
SeqList <T>::~ SeqList()
{
delete[] _data ;
}
//全特化:
template <>
class SeqList <int>
{
public :
SeqList(int capacity);
~ SeqList();
private :
int _size ;
int _capacity ;
int* _data ;
};
// 特化後定義成員函式、成員函式不再需要模板形參列表
SeqList <int>:: SeqList(int capacity)
: _size(0)
, _capacity(capacity )
, _data(new int[ _capacity])
{
cout<<"SeqList<int>" <<endl;
}
SeqList <int>::~ SeqList()
{
delete[] _data ;
}
void test1 ()
{
SeqList<double > sl2;
SeqList<int > sl1(2);
}
//偏特化(部分特化)
template <typename T1, typename T2>
class Data
{
public :
Data();
};
template <typename T1, typename T2>
Data<T1 , T2>::Data()
{
cout<<"Data<T1,T2>"<<endl;
}
// 區域性特化第二個引數為某個具體的型別,如int 、 double 等
template <typename T1>
class Data <T1, int>
{
public :
Data();
};
template <typename T1>
Data<T1 , int>::Data()
{
cout<<"Data<T1,int>"<<endl;
}
下面的例子可以看出,偏特化並不僅僅是指特化部分引數,而是針對模板引數更進一步的條件限制所設計出來的一個特化版本。
// 區域性特化兩個引數為指標型別
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public :
Data();
};
template <typename T1, typename T2>
Data<T1 *, T2*>:: Data()
{
cout<<"Data<T1*,T2*>"<<endl;
}
// 區域性特化兩個引數為引用
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public :
Data(const T1& d1, const T2& d2);
};
template <typename T1, typename T2>
Data<T1 &, T2&>:: Data(const T1& d1, const T2& d2)
{
cout<<"Data<T1&,T2&>"<<endl;
}
void Test()
{
Data<double , int> d1;
Data<int , double> d2;
Data<int *, int*> d3;
Data<int&, int&> d4(1, 2);
}
相關推薦
C++模板剖析:函式模板、類模板解析
C++中關於模板&泛型程式設計問題: 問題引入:何編寫一個通用加法函式? (1)使用函式過載,針對每個所需相同行為的不同型別重新實現它 int Add(const int &_iLeft, const int&_iRight) { return
淺談C++ templates 函式模板、類模板以及非型別模板引數
最近打算挑選幾個STL容器做個簡單實現,發現裡面牽涉到不少模板知識。這裡算提前學習一下C++模板的相關知識吧。這次主要學習了什麼是函式模板(這個最簡單),類模板以及非型別模板引數。下面挨個舉例說明。 文章目錄 1. 函式模板 2. 類
C++自定義模板(函式模板、類模板)
C++提供兩種模板機制:函式模板、類模板一、函式模板1、所謂函式模板,實際上是建立一個通用函式,其函式型別和形參型別不具體指定,用一個虛擬的型別來代表。這個通用函式就稱為函式模板。凡是函式體相同的函式都可以用這個模板來代替,不必定義多個函式,只需在模板中定義一次即可。在呼叫函
C++複習筆記(六)之函式指標和函式模板、類模板
一、函式指標 函式指標在C語言中的作用類似於c++中的多型,都是可以實現框架的搭建,程式碼的相容性高。 函式三要素:名稱、引數、返回值 C語言可以通過typedef為函式型別重新命名,語法 typedef 返回值型別(型別名稱)(引數列表);如下程式碼所示: #in
C/C++ 指標陣列與陣列指標、函式指標與指標函式、模板函式與函式模板、類模板與模板類區別
函式模板與模板函式、模板類與類模板區別: 在C++中有好幾個這樣的術語很重要: 函式指標——指標函式陣列指標——指標陣列類模板——模板類函式模板——模板函式 1.函式指標——指標函式 函式指標的重點是指標。表示的是一個指標,它指向的是一個函式,例子: int
C++模板、類模板、函式模板詳解都在這裡了
一、引言 在寫排序演算法時,如果要寫一個交換兩個資料的函式,由於基本資料型別有int、float、double等等型別,所以針對每
C++ 模板常見特性(函式模板、類模板)
背景 C++ 是很強大,有各種特性來提高程式碼的可重用性,有助於減少開發的程式碼量和工作量。 C++ 提高程式碼的可重用性主要有兩方面: 繼承 模板 繼承的特性我已在前面篇章寫過了,本篇主要是說明「模板」的特性。 使用「模板」的特性設計,實際上也就是「泛型」程式設計。 函式模板 01 變數交換函式模板 假
模板、函式模板、類模板
一、模板 泛型(Generic Programming)即是指具有在多種資料型別上皆可操作的含意。泛型程式設計的代表作品 STL 是一種高效、泛型、可互動操作的軟體元件。 泛型程式設計最初誕生於 C++中,目的是為了實現 C++的 STL(標準模板庫)。其語言支援機制就是模板(Templates)
C++筆記 第五十九課 類模板深度剖析---狄泰學院
如果在閱讀過程中發現有錯誤,望評論指正,希望大家一起學習,一起進步。 學習C++編譯環境:Linux 第五十九課 類模板深度剖析 1.多引數類模板 類模板可以定義任意多個不同的型別引數 類模板可以被特化 指定類模板的特定實現 部分型別引數必須顯示指定 根據型別引數分開實現類
c++---非型別的類模板引數、類模板的特化、模板的分離編譯
一. 非型別的類模板函式 在類的模板引數列表中,不一定是都是型別,或者介面卡,也可以是一個數。 //一般都使用int,用作規定大小 template <class T,size_t MAXSIZE> 具體使用: template
淺談static、類模板和函式模板
首先,我們先來看static,從C語言我們知道,他是一個靜態變數的識別符號,可以讓一個變數在離開作用域後,還能夠存活。那麼如果我們在類當中定義了一個static資料會怎麼樣,這個static資料會在程式開始前就會存在,而且僅此一份。那麼static function呢,簡而
模板——函式模板、類模板
模板 函式模板使程式設計師能夠用單段程式碼指定相關(過載)函式的全部範圍,稱為函式模板特殊化; 類模板使程式設計師能夠用單段程式碼指定相關類的全部範圍,稱為類模板特殊化。 什麼是泛型程式設計? STL方法允許編寫通用的程式,使得程式碼不依賴於底層的容器。這種程式設計風格
C++網易雲課堂開發工程師--類模板
vat turn 全部 name 實現 temp mes ima 獲取 static complex data members static data members member functions static member functions 非靜態成員函數:non
函式模版和類模板的使用
template template用於過載(overriding),目的是讓形參型別不同的函式可以共用一個類名或者函式名。 最簡單的使用,對一個函式進行過載,引數是可變的 原型: template <class identifier> functio
Django(七)—— 模板層:變量、過濾器、標簽、自定義標簽和過濾器
繼續 我們 safe 字符串格式化 html標記 源文件 lur fad pri 模板層:變量、過濾器、標簽、自定義標簽和過濾器 將頁面的設計和Python的代碼分離開會更幹凈簡潔更容易維護。 我們可以使用 Django的 模板系統 (Template System)來實現
vue模板語法:插值、指令
插值:{{}} 指令:v- (在{{}}和v-指令進行資料繫結時,支援js單個表示式) <p v-text='msg'></p>,就相當於插值表示式的功能 <p v-html='title'></p>,可以解析標籤 data:{
C++筆記 第六十課 陣列類模板---狄泰學院
如果在閱讀過程中發現有錯誤,望評論指正,希望大家一起學習,一起進步。 學習C++編譯環境:Linux 第六十課 陣列類模板 1.預備知識 模板引數可以是數值型引數(非型別引數) 數值型模板引數的限制 變數不能作為模板引數 浮點數不能作為模板引數 類物件不能作為模板引數 。。
C++筆記 第五十八課 類模板的概念和意義---狄泰學院
如果在閱讀過程中發現有錯誤,望評論指正,希望大家一起學習,一起進步。 學習C++編譯環境:Linux 第五十八課 類模板的概念和意義 1.思考 在C++中是否能夠將泛型的思想應用於類? 2.類模板 一些類主要用於儲存和組織資料元素 類中資料組織的方式和資料元素的具體型
對C++ templates類模板的幾點補充(Traits類模板特化)
前一篇文章《淺談C++ templates 函式模板、類模板以及非型別模板引數》簡單的介紹了什麼是函式模板(這個最簡單),類模板以及非型別模板引數。本文對類模板再做幾點補充。 文章目錄1. 預設的模板實參2. Traits程式設計技法——以STL迭代器為例1. 預設的模板實參這裡依舊使用上一篇文章中的arr
C語言:函式指標、函式指標陣列、函式指標陣列的指標
一.函式指標 1.定義理解 函式指標:是一個指標,存放函式地址的指標。 函式的地址可以通過取地址函式名拿到,也可以通過函式名直接拿到。 2.函式指標 (1)定義一個函式test void test(char *str) { printf("