C++函數模板詳解


1.2 函數模板

  • C++另一種編程思想稱為 泛型編程 ,主要利用的技術就是模板

  • C++提供兩種模板機制:函數模板類模板

1.2.1 函數模板語法

函數模板作用:

建立一個通用函數,其函數返回值類型和形參類型可以不具體制定,用一個虛擬的類型來代表。

語法:

template<typename T>
函數聲明或定義

解釋:

template --- 聲明創建模板

typename --- 表面其后面的符號是一種數據類型,可以用class代替

T --- 通用的數據類型,名稱可以替換,通常為大寫字母

示例:

//交換整型函數
void swapInt(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

//交換浮點型函數
void swapDouble(double& a, double& b) {
	double temp = a;
	a = b;
	b = temp;
}

//利用模板提供通用的交換函數
template<typename T>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

void test01()
{
	int a = 10;
	int b = 20;
	
	//swapInt(a, b);

	//利用模板實現交換
	//1、自動類型推導
	mySwap(a, b);

	//2、顯示指定類型
	mySwap<int>(a, b);

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

}

int main() {

	test01();

	system("pause");

	return 0;
}

總結:

  • 函數模板利用關鍵字 template
  • 使用函數模板有兩種方式:自動類型推導、顯示指定類型
  • 模板的目的是為了提高復用性,將類型參數化

1.2.2 函數模板注意事項

注意事項:

  • 自動類型推導,必須推導出一致的數據類型T,才可以使用

  • 模板必須要確定出T的數據類型,才可以使用

示例:

//利用模板提供通用的交換函數
template<class T>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}


// 1、自動類型推導,必須推導出一致的數據類型T,才可以使用
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';

	mySwap(a, b); // 正確,可以推導出一致的T
	//mySwap(a, c); // 錯誤,推導不出一致的T類型
}


// 2、模板必須要確定出T的數據類型,才可以使用
template<class T>
void func()
{
	cout << "func 調用" << endl;
}

void test02()
{
	//func(); //錯誤,模板不能獨立使用,必須確定出T的類型
	func<int>(); //利用顯示指定類型的方式,給T一個類型,才可以使用該模板
}

int main() {

	test01();
	test02();

	system("pause");

	return 0;
}

總結:

  • 使用模板時必須確定出通用數據類型T,並且能夠推導出一致的類型

1.2.3 函數模板案例

案例描述:

  • 利用函數模板封裝一個排序的函數,可以對不同數據類型數組進行排序
  • 排序規則從大到小,排序算法為選擇排序
  • 分別利用char數組int數組進行測試

示例:

//交換的函數模板
template<typename T>
void mySwap(T &a, T&b)
{
	T temp = a;
	a = b;
	b = temp;
}


template<class T> // 也可以替換成typename
//利用選擇排序,進行對數組從大到小的排序
void mySort(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		int max = i; //最大數的下標
		for (int j = i + 1; j < len; j++)
		{
			if (arr[max] < arr[j])
			{
				max = j;
			}
		}
		if (max != i) //如果最大數的下標不是i,交換兩者
		{
			mySwap(arr[max], arr[i]);
		}
	}
}
template<typename T>
void printArray(T arr[], int len) {

	for (int i = 0; i < len; i++) {
		cout << arr[i] << " ";
	}
	cout << endl;
}
void test01()
{
	//測試char數組
	char charArr[] = "bdcfeagh";
	int num = sizeof(charArr) / sizeof(char);
	mySort(charArr, num);
	printArray(charArr, num);
}

void test02()
{
	//測試int數組
	int intArr[] = { 7, 5, 8, 1, 3, 9, 2, 4, 6 };
	int num = sizeof(intArr) / sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}

int main() {

	test01();
	test02();

	system("pause");

	return 0;
}

總結:模板可以提高代碼復用,需要熟練掌握

1.2.4 普通函數與函數模板的區別

普通函數與函數模板區別:

  • 普通函數調用時可以發生自動類型轉換(隱式類型轉換)
  • 函數模板調用時,如果利用自動類型推導,不會發生隱式類型轉換
  • 如果利用顯示指定類型的方式,可以發生隱式類型轉換

示例:

//普通函數
int myAdd01(int a, int b)
{
	return a + b;
}

//函數模板
template<class T>
T myAdd02(T a, T b)  
{
	return a + b;
}

//使用函數模板時,如果用自動類型推導,不會發生自動類型轉換,即隱式類型轉換
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	
	cout << myAdd01(a, c) << endl; //正確,將char類型的'c'隱式轉換為int類型  'c' 對應 ASCII碼 99

	//myAdd02(a, c); // 報錯,使用自動類型推導時,不會發生隱式類型轉換

	myAdd02<int>(a, c); //正確,如果用顯示指定類型,可以發生隱式類型轉換
}

int main() {

	test01();

	system("pause");

	return 0;
}

總結:建議使用顯示指定類型的方式,調用函數模板,因為可以自己確定通用類型T

1.2.5 普通函數與函數模板的調用規則

調用規則如下:

  1. 如果函數模板和普通函數都可以實現,優先調用普通函數
  2. 可以通過空模板參數列表來強制調用函數模板
  3. 函數模板也可以發生重載
  4. 如果函數模板可以產生更好的匹配,優先調用函數模板

示例:

//普通函數與函數模板調用規則
void myPrint(int a, int b)
{
	cout << "調用的普通函數" << endl;
}

template<typename T>
void myPrint(T a, T b) 
{ 
	cout << "調用的模板" << endl;
}

template<typename T>
void myPrint(T a, T b, T c) 
{ 
	cout << "調用重載的模板" << endl; 
}

void test01()
{
	//1、如果函數模板和普通函數都可以實現,優先調用普通函數
	// 注意 如果告訴編譯器  普通函數是有的,但只是聲明沒有實現,或者不在當前文件內實現,就會報錯找不到
	int a = 10;
	int b = 20;
	myPrint(a, b); //調用普通函數

	//2、可以通過空模板參數列表來強制調用函數模板
	myPrint<>(a, b); //調用函數模板

	//3、函數模板也可以發生重載
	int c = 30;
	myPrint(a, b, c); //調用重載的函數模板

	//4、 如果函數模板可以產生更好的匹配,優先調用函數模板
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2); //調用函數模板
}

int main() {

	test01();

	system("pause");

	return 0;
}

總結:既然提供了函數模板,最好就不要提供普通函數,否則容易出現二義性

1.2.6 模板的局限性

局限性:

  • 模板的通用性並不是萬能的

例如:

	template<class T>
	void f(T a, T b)
	{ 
    	a = b;
    }

在上述代碼中提供的賦值操作,如果傳入的a和b是一個數組,就無法實現了

再例如:

	template<class T>
	void f(T a, T b)
	{ 
    	if(a > b) { ... }
    }

在上述代碼中,如果T的數據類型傳入的是像Person這樣的自定義數據類型,也無法正常運行

因此C++為了解決這種問題,提供模板的重載,可以為這些特定的類型提供具體化的模板

示例:

#include<iostream>
using namespace std;

#include <string>

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};

//普通函數模板
template<class T>
bool myCompare(T& a, T& b)
{
	if (a == b)
	{
		return true;
	}
	else
	{
		return false;
	}
}


//具體化,顯示具體化的原型和定意思以template<>開頭,並通過名稱來指出類型
//具體化優先於常規模板
template<> bool myCompare(Person &p1, Person &p2)
{
	if ( p1.m_Name  == p2.m_Name && p1.m_Age == p2.m_Age)
	{
		return true;
	}
	else
	{
		return false;
	}
}

void test01()
{
	int a = 10;
	int b = 20;
	//內置數據類型可以直接使用通用的函數模板
	bool ret = myCompare(a, b);
	if (ret)
	{
		cout << "a == b " << endl;
	}
	else
	{
		cout << "a != b " << endl;
	}
}

void test02()
{
	Person p1("Tom", 10);
	Person p2("Tom", 10);
	//自定義數據類型,不會調用普通的函數模板
	//可以創建具體化的Person數據類型的模板,用於特殊處理這個類型
	bool ret = myCompare(p1, p2);
	if (ret)
	{
		cout << "p1 == p2 " << endl;
	}
	else
	{
		cout << "p1 != p2 " << endl;
	}
}

int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

總結:

  • 利用具體化的模板,可以解決自定義類型的通用化
  • 學習模板並不是為了寫模板,而是在STL能夠運用系統提供的模板


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM