C++ 模板

當我們在編寫一個通用的加法時,首先我們會想到函數重載,就像下面這段代碼

int Add(const int left,const int right)
{
	return left + right;
}

float Add(const float left,const float right)
{
	return left + right;
}


int main()
{
	cout<<Add(1,2)<<endl;
	cout<<Add((float)1.2,(float)2.0)<<endl;
	system("pause");
	return 0;
}
但是當函數需要的類型多了,就要不斷的重載不同類型重新實現它。這樣做存在一些問題:

1、因爲除類型外所有函數的函數體都相同,所以代碼複用率不高。

2、只有返回值不同不能重載。

3、只要有新類型出現,就要重新添加對應函數。

4、一個方法有問題,所有方法都有問題,不好維護。

那我們考慮是否有別的辦法,使用公共基類,將通用的代碼放在公共的基類中。但是這是藉助公共基類來編寫通用代碼,將失去類型檢查的優點;而且對於以後實現的許多類,都必須繼承自某個特定的基類,代碼維護更加困難,速度也比較慢。

並且函數重載和公共基類都需要我們自己來寫容易出錯。

那麼我們還可以使用宏函數哈哈但是宏函數不進行參數類型檢測,安全性不高。

所以,綜上所述 我們可以採用的最好的辦法就是泛型編程了。

什麼是泛型編程呢,泛型編程就是編寫與類型無關的邏輯代碼,是代碼複用的一種手段。模板是泛型編程的基礎。模板中包含了函數模板和類模板。

函數模板:代表了一個函數家族,該函數與類型無關,在使用時被參數化,根據實參類型產生函數的特定類型版本。但模板本身不是一個類/函數,編譯器用模板產生指定的類或函數。


下面來看一個模板實例


注:這段代碼中模板被編譯了兩次:

1、實例化之前,檢查模板代碼本身,查看是否出現語法錯誤,如:遺漏分號

2、在實例化期間,檢查模板代碼,查看是否所有的調用都有效,如:實例化類型不支持某些函數調用


實參推演:函數實參確定模板形參類型的過程稱爲模板實參推斷,多個類型形參的實參必須完全匹配。

類型形參轉換:一般不會轉換實參以匹配已有的實例化,相反會產生新的實例。

如:cout<<Add(1,/*(int)*/'1')<<endl;//必須有帶兩個參數的模板,會產生新的實例:int Add(int left,char right)

編譯器只會執行兩種轉換:

1、const轉換:接收const引用或者const指針的函數可以分別用非const對象的引用或者指針來調用

template <typename T>
void FunTest(const T& a)//這裏的const修飾a,表示a不能被修改,a的類型爲int* const
{                       //const int* p ;const修飾*p
	                //int* const p ;const修飾p
	//int b = 5;
	//a = &b;//a被const修飾,a相當於int*類型,它的指向不能被修改

	*a = 20;//解引用不會出錯,因爲a的內容可以被修改
	cout<<*a<<endl;
}
int main()
{
	int a = 10;//這裏的a沒有用const修飾,可以用a調用FunTest(const T& a)即可以轉化
	FunTest(&a);
	system("pause");
	return 0;
}

2、數組或函數到指針的轉換:如果模板形參不是引用類型,則對數組或函數類型的實參應用常規指針轉換。數組實參將當做指向其第一個元素的指針,函數實參當做指向函數類型的指針。

void Fun()
{
	cout<<"Fun()"<<endl;
}
template<typename T>
void FunTest(T p)
{
	cout<<typeid(p).name()<<endl;//數組實參當做指向其第一個元素的指針,p爲int*類型
	//(*p)();//p爲void (__cdecl*)(void)類型,函數指針調用函數
	p();//同(*p)()一樣
	cout<<"FunTest()"<<endl;
}
int main()
{
	//int arr[10] = {0};
	//FunTest(arr);
	FunTest(Fun);
	system("pause");
	return 0;
}

模板參數:

函數模板有兩種類型參數:模板參數和調用參數。

模板形參包括類型形參非類型形參

//模板形參名字只能在模板形參之後到模板聲明或定義的末尾之間使用,遵循名字屏蔽規則
typedef int T;
T g_a;

template<typename T>
void FunTest(T p)
{
	T a;
	cout<<typeid(a).name()<<endl;//int
}
int main()
{
	FunTest(1);//優先用模板參數列表中的T
    cout<<typeid(g_a).name()<<endl;//int
	system("pause");
	return 0;
}
模板形參的名字在同一模板形參列表中只能使用一次,template<typename T,typename T>會報錯重定義參數T

所有模板形參前面必須加上class或者typename關鍵字修飾。

在函數模板的內部不能指定缺省的模板實參。

非模板類型參數


template<class T, int N>
//void FunTest(T array)-->用FunTest<int[3],3>(a)調用;
void FunTest(T (&array) [N])
{
	int idx = 0;
	for(idx = 0; idx < N; idx++)
	{
		array[idx] = 0;
	}
}
int main()
{
	int a[3];
	float b[5];
	FunTest(b);
	FunTest(a);
	//FunTest<int,3>(a);//相當於FunTest(a)
	system("pause");
	return 0;
}

模板形參一些說明:

1、模板形參表使用<>括起來。

2、和函數參數表一樣,跟多個參數時必須用逗號隔開,類型可以相同也可以不相同。

3、定義模板函數時模板形參表不能爲空。

4、模板形參可以是類型形參,也可以是非類型新參,類型形參跟在class和typename後。

5、模板類型形參可作爲類型說明符用在模板中的任何地方,與內置類型或自定義類型使用方法完全相同,可用於指定函數形參類型、返回值、局部變量和強制類型轉換。

6、模板形參表中,class和typename具有相同的含義,可以互換,使用typename更加直觀。但關鍵字typename是作爲C++標準加入到C++中的,舊的編譯器可能不支持。

模板函數重載及模板函數特化

【重載說明】函數的所有重載版本的聲明都應該位於該函數被調用位置之前。

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

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

3、顯式指定一個空的模板實參列表,該語法告訴編譯器只有模板才能來匹配這個調用,而且所有的模板參數都應該根據實參演繹出來。

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

///////////////////模板重載//////////////////////////////
template <typename T>
T Max(const T& left,const T& right)
{
	cout<<typeid(left).name()<<endl;
	return (left > right) ? left : right;
}
template <typename T>
T Max(const T& left,const T& right,const T& mid)
{
	T temp = Max(left,right);
	return (temp>mid) ? temp : mid;
}
//const char* Max(char* const left, char* const right)
//{
//	if(strcmp(left,right)>0)
//		return left;
//	return right;
//}
int main()
{
	cout<<Max(1,2,3)<<endl;
	cout<<Max<int>(2,4.5)<<endl;
	cout<<Max(2.4,4.6)<<endl;
	char* p1 = "hello";
	char* p2 = "world";
	cout<<Max(p1,p2)<<endl;
	system("pause");
	return 0;
}

上面這段代碼就是模板函數的重載,但是比較兩個字符串會出錯,出錯的原因如下圖:



模板總結:

【優點】模板複用了代碼,節省資源,更快的迭代開發,C++的標準模板庫(STL)因此而產生。增強了代碼的靈活性。【缺點】模板讓代碼變得凌亂複雜,不易維護,編譯代碼時間變長。出現模板編譯錯誤時,錯誤信息非常凌亂,不易定位錯誤。








發佈了62 篇原創文章 · 獲贊 14 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章