C++提高程式設計---1 模闆【P167~P184】
- 1 模闆
-
- 1.1 函數模闆
-
- 1.1.1 函數模闆文法
- 1.1.2 函數模闆注意事項
- 1.1.3 函數模闆案例
- 1.1.4 普通函數與函數模闆差別
- 1.1.5 普通函數與函數模闆的調用規則
- 1.1.6 模闆的局限性
【C++ 提高程式設計主要針對 C++ 泛型程式設計和 STL技術作詳細講解,探讨C++更深層次的應用】
1 模闆
模闆就是建立通用的模具,大大提高複用性。
模闆的特點:
- 模闆不可以直接使用,隻是一個架構;
- 模闆雖然具有通用性,但并不是萬能的。
1.1 函數模闆
- C++ 另一種程式設計思想被稱為泛型程式設計,主要利用的技術就是模闆
- C++ 提供兩種模闆機制:函數模闆和類模闆
1.1.1 函數模闆文法
函數模闆作用:
建立一個通用的函數,其函數傳回值類型和形參類型可以不具體制定,用一個虛拟的類型來代表。
實作整型、浮點型資料交換函數如下:
# include<iostream>
using namespace std;
// 函數模闆
// 交換兩個整型函數
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;
}
void test01()
{
int a = 20;
int b = 10;
swapInt(a,b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
double c = 1.1;
double d = 2.2;
swapDouble(c, d);
cout << "c = " << c << endl;
cout << "d = " << d << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
雖然上述代碼可以實作相應的功能,但是如果讓你實作所有資料類型的交換實作呢?顯然一個個都實作出來是太 low 的,更何況還會有使用者自己定義的資料類型,想要全部實作的話就會顯得力不從心了。
通過觀察交換整型交換浮點型函數可以發現,兩函數存在差別的地方在于資料類型,而代碼實作都是一樣的,是以,我們可以先将資料類型部分用一個大寫字母(比如T)來表示它,後期使用的時候再告訴這個資料類型具體應該是什麼。
下面是函數模闆實作方式:
# include<iostream>
using namespace std;
// 函數模闆
template<typename T> // 聲明一個模闆,告訴編譯器後面代碼中緊跟着的T不要報錯,T是一個通用的資料類型
void mySwap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
// 利用函數模闆實作交換
void test02()
{
// 兩種方式實作函數模闆調用
// 1、自動類型推導
int a = 20;
int b = 10;
mySwap(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
// 2、顯示指定類型
double c = 1.1;
double d = 2.2;
mySwap<double>(c, d);
cout << "c = " << c << endl;
cout << "d = " << d << endl;
}
int main()
{
test02();
system("pause");
return 0;
}
總結:
- 函數模闆利用關鍵字 template;
- 使用函數模闆有兩種方式:自動類型推導、顯示指定類型;
- 模闆的目的是為了提高複用性,将類型參數化。
1.1.2 函數模闆注意事項
注意事項:
- 自動類型推導,必須推導出一緻的資料類型T,才可以使用
- 模闆必須要确定出T的資料類型,才可以使用
1.1.3 函數模闆案例
# include<iostream>
using namespace std;
// 交換函數模闆
template<class T>
void mySwap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
template<class T>
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)
{
mySwap(arr[max], arr[i]);
}
}
}
// 提供列印數組模闆
template <class T>
void printArray(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void test01()
{
// 測試 char 數組
char charArr[] = "badcfe";
int num = sizeof(charArr) / sizeof(char);
mySort(charArr, num);
printArray(charArr, num);
}
void test02()
{
// 測試 int 數組
int intArr[] = {7, 5, 1, 3, 9, 2, 4, 6, 8};
int num = sizeof(intArr) / sizeof(int);
mySort(intArr, num);
printArray(intArr, num);
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
1.1.4 普通函數與函數模闆差別
自動類型轉換==隐式類型轉換
1、普通函數調用時可以發生隐式類型轉換
# include<iostream>
using namespace std;
// 普通函數
int myAdd01(int a, int b)
{
return a + b;
}
void test01()
{
int a = 10;
int b = 20;
cout << myAdd01(a, b) << endl;
char c = 'a';
cout << myAdd01(a, c) << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
可以看到,當輸入參數是 char 型變量時,程式也能正常運作,是在運算過程中隐式地将字元型變量轉換成了整型變量(對應的 Ascall 碼)去計算。
2、函數模闆在使用自動類型推導的時候不可以發生隐式類型轉換,而在利用指定類型的方式,可以發生隐式類型轉換
可以發現函數模闆在使用自動類型推導時報錯,而采用指定類型方式不報錯。
1.1.5 普通函數與函數模闆的調用規則
# include<iostream>
using namespace std;
// 普通函數
void myPrint(int a, int b)
{
cout << "調用的普通函數" << endl;
}
// 函數模闆
template<class T>
void myPrint(T a, T b)
{
cout << "調用的函數模闆" << endl;
}
// 重載的函數模闆
template<class T>
void myPrint(T a, T b, T c)
{
cout << "調用的重載的函數模闆" << endl;
}
void test01()
{
int a = 10;
int b = 20;
// 1、如果普通函數和函數模闆都可以實作,那麼優先調用普通函數
myPrint(a, b);
// 2、可以通過空模闆參數清單來強制調用函數模闆
myPrint<>(a, b);
// 3、函數模闆也可以發生函數重載
myPrint(a, b, 100);
// 4、如果函數模闆可以産生更好的比對,那麼優先調用函數模闆
char c1 = 'a';
char c2 = 'c';
myPrint(c1, c2);
/*
我們知道雖然普通函數可以發生隐式類型轉換,但是這裡函數模闆更加比對,不用再進行隐式類型轉換了,是以優先調用函數模闆
*/
}
int main()
{
test01();
system("pause");
return 0;
}
總結:既然已經提供了函數模闆,最好就不要再提供普通函數了,否則容易出現二義性
1.1.6 模闆的局限性
局限性:模闆的通用性不是萬能的!!
是以,C++ 為了解決這種問題,提供了模闆的重載,可以為這些特定的類型提供具體化的模闆
# include<iostream>
# include<string>
using namespace std;
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;
}
}
// 利用具體化 Person 的版本實作代碼,具體化優先調用
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);
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 能夠運用系統提供的模闆。