1. 程式人生 > >C++函式模板及實現原理

C++函式模板及實現原理

    C++為我們提供了函式模板機制。所謂函式模板,實際上是建立一個通用函式,其函式型別和形參型別不具體指定,用一個虛擬的型別來代表。這個通用函式就稱為函式模板。

    凡是函式體相同的函式都可以用這個模板來代替,不必定義多個函式,只需在模板中定義一次即可。在呼叫函式時系統會根據實參的型別來取代模板中的虛擬型別,從而實現了不同函式的功能。

    為什麼要有函式模板

    下面,我們就通過一個例子來說明為什麼需要有函式模板。

    需求:寫n個函式,交換char型別、int型別、double型別變數的值。

    如果不適用函式模板的話,我們的程式碼需要這樣寫:

void swap(int &a, int &b)
{
	int t = a;
	a = b;
	b = t;
}

void swap(char &a, char &b)
{
	char t = a;
	a = b;
	b = t;
}
    這樣的程式碼總是很麻煩,幾乎一樣的程式碼卻要重複寫很多次,因為,我們就出現了函式模板機制。有了函式模板之後,我們的程式碼可以這樣寫:   

#include<iostream>
using namespace std;

//template 關鍵字告訴C++編譯器 我要開始泛型了.你不要隨便報錯  
//資料型別T 引數化資料型別
template <typename T>
void myswap(T &a, T &b)
{
	T t;
	t = a;
	a = b;
	b = t;
}

void main()
{
	int  x = 1;
	int	 y = 2;
	myswap(x, y); //自動資料型別 推導的方式 
	
	float a = 2.0;
	float b = 3.0;
	myswap(a, b); //自動資料型別 推導的方式 
	
	myswap<float>(a, b); //顯示型別呼叫 

	cout<<"hello..."<<endl;
	return ;
}
    我們可以看到,這樣就可以大大減少程式碼量,讓我們程式設計變得更加方便。

  函式模板語法

    函式模板定義形式:template<型別形式引數表>

    型別形式引數的形式為:typename T1 ,  typename T2 , …… , typename Tn   或  class T1 ,  class

 T2 , …… , class Tn

    

    函式模板呼叫

myswap(x, y); //自動資料型別 推導的方式 
	
myswap<float>(a, b); //顯示型別呼叫 
    

    函式模板做函式引數

    函式模板是可以作為函式引數的,我們可以寫一段簡單的排序程式碼來驗證。

#include <iostream>
using namespace std;

template<typename T, typename T2>
void sortArray(T *a, T2 num)
{
	T tmp ;
	int i, j ;
	for (i=0; i<num; i++)
	{
		for (j=i+1; j<num; j++)
		{
			if (a[i] < a[j])
			{
				tmp = a[i];
				a[i] = a[j];
				a[j] = tmp;
			}
		}
	}
}

template<class T>
void pirntArray(T *a, int num)
{
	int i = 0;
	for (i=0; i<num; i++)
	{
		cout<<a[i]<<" ";
	}
	cout << endl;
}

int main()
{
	int num = 0;
	char a[] = "ddadeeettttt";
	num = strlen(a);

	cout << "排序之前" << endl;
	pirntArray<char>(a, num);

	sortArray<char, int>(a, num); //顯示型別呼叫 模板函式 <>
	cout << "排序之後" << endl;
	pirntArray<char>(a, num);
	cout<<"hello..."<< endl;
	return 0;
}
    最後輸出結果:

    

    函式模板遇上函式過載

    函式模板和普通函式的區別:函式模板是不允許自動型別轉換的,而普通函式允許自動型別轉換

    當函式模板和普通函式在一起時,呼叫規則如下

  1.     函式模板可以像普通函式一樣被過載
  2.     c++編譯器優先考慮普通函式
  3.     如果函式模板可以產生一個更好的匹配,那麼選擇模板
  4.     可以通過空模板實參列表的語法,限定編譯器只通過模板匹配
   下面我將通過程式碼來演示這個過程:
#include <iostream>
using namespace std;

template <typename T>
void myswap(T &a, T &b)
{
	T t;
	t = a;
	a = b;
	b = t;
	cout<<"myswap 模板函式do"<<endl;
}

void myswap(char &a, int &b)
{
	int t;
	t = a;
	a = b;
	b = t;
	cout<<"myswap 普通函式do"<<endl;
}

int main()
{
	char cData = 'a';
	int  iData = 2;

	myswap<int>(cData, iData);  //結論 函式模板不提供隱式的資料型別轉換  必須是嚴格的匹配

	//myswap(cData, iData); 
	//myswap(iData, cData);
	
	cout<<"hello..."<<endl;
	return 0;
}
    當我們執行如上程式碼時,編譯器會報錯           由此,我們就能得出結論:函式模板不提供隱式的型別轉換,必須是嚴格的匹配    接下來我們繼續看另一段程式碼:
#include <iostream>
using namespace std;

int Max(int a, int b)
{
	cout<<"int Max(int a, int b)"<<endl;
	return a > b ? a : b;
}

template<typename T>
T Max(T a, T b)
{
	cout<<"T Max(T a, T b)"<<endl;
	return a > b ? a : b;
}

template<typename T>
T Max(T a, T b, T c)
{
	cout<<"T Max(T a, T b, T c)"<<endl;
	return Max(Max(a, b), c);
}


int main()
{
	int a = 1;
	int b = 2;

	cout<<Max(a, b)<<endl; //當函式模板和普通函式都符合呼叫時,優先選擇普通函式
	
	cout<<Max<>(a, b)<<endl; //若顯示使用函式模板,則使用<> 型別列表

	cout<<Max(3.0, 4.0)<<endl; //如果 函式模板產生更好的匹配 使用函式模板

	cout<<Max(5.0, 6.0, 7.0)<<endl; //過載

	cout<<Max('a', 100)<<endl;  //呼叫普通函式 可以隱式型別轉換 
	return 0;
}
    執行結果如下:              c++編譯器模板機制實現     通過上面的學習,我們會產生一個問題:為什麼函式模板可以和函式過載可以放在一塊。c++編譯器是如何提供函式模板機制的?     我們可以通過觀察反彙編來解決這個問題,由於彙編程式碼過長,所以這裡就不貼了,大家有興趣可以自己試試。     通過觀察反彙編程式碼,我們可以得出這樣的結論:編譯器並不是把函式模板處理成能夠處理任意類的函式;編譯器從函式模板通過具體型別產生不同的函式;編譯器會對函式模板進行兩次編譯在宣告的地方對模板程式碼本身進行編譯,在呼叫的地方對引數替換後的程式碼進行編譯