C++ 函式模板和類模板--泛型程式設計
所謂函式模板,實際上是建立一個通用函式,其函式型別和形參型別不具體指定,用一個虛擬的型別來代表。這個通用函式就稱為函式模板。
凡是函式體相同的函式都可以用這個模板來代替,不必定義多個函式,只需在模板中定義一次即可。
一 函式模板初識
1) 為什麼要有函式模板?
函式業務邏輯一樣,但是函式引數型別不一樣,引入泛型程式設計,方便程式設計師程式設計。
2) 語法:
template <typename T>
void myswap(T &a,T &b)
{
}
a: tempalte是告訴C++要進行泛程式設計,看到T不要隨便報錯。
b :T表示型別
3) 呼叫
a:顯式呼叫:
myswap(x,y);
myswap(a,b);
b: 自動類推導
myswap(x,y);//根據引數型別來自動匹配
#include <iostream> using namespace std; // 函式的業務邏輯 一樣 // 函式的引數型別 不一樣 void myswap01(int &a, int &b) { int c = 0; c = a; a = b; b = c; } void myswap02(char &a, char &b) { char c = 0; c = a; a = b; b = c; } template <typename T> void myswap(T &a, T &b) { T c = 0; c = a; a = b; b = c; cout << "hello ....我是模板函式 歡迎 calll 我" << endl; } void main() { { int x = 10; int y = 20; myswap<int>(x, y); //1 函式模板 顯示型別 呼叫 myswap(x, y); //2 自動型別 推導 printf("x:%d y:%d \n", x, y); } { char a = 'a'; char b = 'b'; myswap<char>(a, b); //1 函式模板 顯示型別 呼叫 myswap(a, b); printf("a:%c b:%c \n", a, b); } }
4 函式模板作函式引數
#include <iostream> using namespace std; //讓int陣列進行排序 template <typename T,typename T2 > int mySort(T *array, T2 size) { T2 i, j ; T tmp; if (array == NULL) { return -1; } //選擇 for (i=0; i<size; i++) { for (j=i+1; j<size; j++) { if (array[i] < array[j]) { tmp = array[i]; array[i] = array[j]; array[j] = tmp; } } } return 0; } template <typename T, typename T2> int myPrint(T *array, T2 size) { T2 i = 0; for (i=0; i<size; i++) { cout << array[i] << " "; } return 0; } void main() { //char 型別 { char buf[] = "aff32ff2232fffffdssss"; int len = strlen(buf); mySort<char, int>(buf, len); myPrint<char , int>(buf, len); } cout<<"hello..."<<endl; system("pause"); return ; }
5 函式模板遇上函式過載
1) 函式模板和普通函式區別:
函式模板不允許自動型別轉化,普通函式能夠進行自動型別轉換
int a = 10;
char c = 'z';
myswap(a, c); // 普通函式的呼叫: 可以進行隱式的型別轉換
myswap(c, a); //這個是時候只會呼叫普通函式
2)函式模板遇上普通函式時的呼叫規則:
a:函式模板可以像普通函式一樣被過載
b:當函式模板和普通函式都符合呼叫時,C++編譯器優先考慮普通函式
c:如果函式模板可以產生一個更好的匹配,那麼選擇模板
d:可以通過空模板實參列表的語法限定編譯器只通過模板匹配,例如:myswap<>(a,b).
#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);
}
void 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; //呼叫普通函式 可以隱式型別轉換
system("pause");
return ;
}
三 編譯器模板機制剖析
1 編譯器並不是把函式模板處理成能夠處理任意類的函式
2 編譯器從函式模板通過具體型別產生不同的函式
3 編譯器會對函式模板進行兩次編譯: 在宣告的地方對模板程式碼本身進行編譯;在呼叫的地方對引數替換後的程式碼進行編譯。
四 類模板初識
1 為什麼需要類模板
1)有時,有兩個或多個類,其功能是相同的,僅僅是資料型別不同
2) 類模板用於實現類所需資料的型別引數化
3) 類模板在表示如陣列、表、圖等資料結構顯得特別重要)
2 單個類模板語法
template <\typename type>
class Tclass{
//至少有一個成員變數為type型別
};
template<\typename T>
class A //A就是一個模板類(或類模板)
{
public:
A(T t)
{
this->t = t;
}
T &getT()
{
return t;
}
private:
T t; };
void main()
{
//模板了中如果使用了建構函式,則遵守以前的類的建構函式的呼叫規則
A<int> a(100); //需要進行型別具體化否則報錯。
a.getT();
printAA(a);
return ; }
3 從模板類派生普通類
子類從模板類繼承的時候,需要讓編譯器知道父類的資料型別具體是什麼
(資料型別的本質:固定大小記憶體塊的別名)A<\int>
4 從模板類派生模板類
template <\typename T>
class C :public A<\T>
{
};
#include <iostream>
using namespace std;
//模板類
template <class T>
class A
{
public:
A(T a)
{
this->a = a;
}
public:
void printA()
{
cout << "a: " << a << endl;
}
protected:
T a;
};
//從模板類 派生了 普通類
// 模板類派生時, 需要具體化模板類. C++編譯器需要知道 父類的資料型別具體是什麼樣子的
//=====> 要知道父類所佔的記憶體大小是多少 只有資料型別固定下來,才知道如何分配記憶體
class B : public A<int>
{
public:
B(int a=10, int b=20) : A<int>(a)//如果父類呼叫了有參建構函式需要顯示初始化,使用物件初始化列表
{
this->b = b;
}
void printB()
{
cout << "a:" << a << " b: " << b << endl;
}
private:
int b;
};
//從模板類 派生 模板類
template <typename T>
class C : public A<T>
{
public:
C(T c, T a) : A<T>(a)
{
this->c = c;
}
void printC()
{
cout << "c:" << c <<endl;
}
protected:
T c;
};
void main()
{
B b1(1, 2);
b1.printB();
C<int> c1(1, 2);
c1.printC();
system("pause");
}
//類模板 做函式引數
//引數 ,C++編譯器 要求具體的類 所以所 要 A<int> &a
void UseA( A<int> &a )
{
a.printA();
}
void main61()
{
//模板類(本身就是型別化的)====具體的類=====>定義具體的變數
A<int> a1(11), a2(20), a3(30); //模板類是抽象的 ====>需要進行 型別具體
UseA(a1);
UseA(a2);
UseA(a3);
cout<<"hello..."<<endl;
system("pause");
return ;
}
5 知識體系
1)所有類模板函式都寫在類的內部
#include <iostream>
using namespace std;
template <typename T>
class Complex
{
friend Complex MySub(Complex &c1, Complex &c2)
{
Complex tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}
friend ostream & operator<<(ostream &out, Complex &c3)
{
out << c3.a << " + " << c3.b << "i" << endl;
return out;
}
public:
Complex(T a, T b)//在類的內部就不用寫template <typename T>
{
this->a = a;
this->b = b;
}
Complex operator+ (Complex &c2)
{
Complex tmp(a+c2.a, b+c2.b);
return tmp;
}
void printCom()
{
cout << "a:" << a << " b: " << b << endl;
}
private:
T a;
T b;
};
//運算子過載的正規寫法
// 過載 << >> 只能用友元函式 ,其他運算子過載 都要寫成成員函式 , 不要濫用友元函式
void main()
{
//需要把模板類 進行具體化以後 才能定義物件 C++編譯器要分配記憶體
Complex<int> c1(1, 2);
Complex<int> c2(3, 4);
Complex<int> c3 = c1 + c2;
//c3.printCom();
cout << c3 << endl;
//濫用友元函式
{
Complex<int> c4 = MySub(c1, c2);
cout << c4 << endl;
}
cout<<"hello..."<<endl;
system("pause");
return ;
}
2)所有的類模板函式寫在類的外部,在一個cpp中
#include <iostream>
using namespace std;
//友元函式:友元函式不是實現函式過載(非<<、>>)
//需要在類前增加類的前置宣告函式的前置宣告
template <typename T>
class Complex ; //類的前置宣告
template <typename T>
Complex<T> MySub (Complex<T> &c1, Complex<T> &c2);
template <typename T>
class Complex
{
friend Complex<T> MySub<T> (Complex<T> &c1, Complex<T> &c2);
friend ostream & operator<< <T> (ostream &out, Complex &c3);//解決方法在此處加<T>
public:
Complex(T a, T b);
void printCom();
Complex operator+ (Complex &c2);
private:
T a;
T b;
};
//建構函式的實現 寫在了類的外部
template <typename T> //每個模板函式前都要加這句
Complex<T>::Complex(T a, T b)//類模板需要具體化
{
this->a = a;
this->b = b;
}
template <typename T>
void Complex<T>::printCom()
{
cout << "a:" << a << " b: " << b << endl;
}
//本質是 : 模板是兩次 編譯生成的 第一次生成的函式頭 和第二次生成的函式頭 不一樣
//成員函式 實現 +運算子過載
template <typename T>
Complex<T> Complex<T>::operator+ (Complex<T> &c2)//類模板需要具體化
{
Complex tmp(a+c2.a, b+c2.b);//此處加不加<T>都可以
return tmp;
}
//友元函式 實現 << 運算子過載
template <typename T>
ostream & operator<<(ostream &out, Complex<T> &c3)
{
out << c3.a << " + " << c3.b << "i" << endl;
return out;
}
//濫用 友元函式。結論:在不要用友元函式的地方不要用友元函式
template <typename T>
Complex<T> MySub(Complex<T> &c1, Complex<T> &c2)
{
Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}
void main()
{
//需要把模板類 進行具體化以後 才能定義物件 C++編譯器要分配記憶體
Complex<int> c1(1, 2);
Complex<int> c2(3, 4);
Complex<int> c3 = c1 + c2;
//c3.printCom();
cout << c3 << endl;
//濫用友元函式
{
Complex<int> c4 = MySub<int>(c1, c2);
cout << c4 << endl;
}
cout<<"hello..."<<endl;
system("pause");
return ;
}
六 類模板中的關鍵字static
/*
編譯器並不是把函式模板處理成能夠處理任意類的函式
編譯器從函式模板通過具體型別產生不同的函式
編譯器會對函式模板進行兩次編譯
在宣告的地方對模板程式碼本身進行編譯;在呼叫的地方對引數替換後的程式碼進行編譯。
*/
#include <iostream>
using namespace std;
template <typename T>
class AA
{
public:
static T m_a;
};
template <typename T>
T AA<T>::m_a = 0;
class AA1
{
public:
static int m_a;
};
int AA1::m_a = 0;
class AA2
{
public:
static char m_a;
};
char AA2::m_a = 0;
void main()
{
AA<int> a1, a2, a3;
a1.m_a = 10;
a2.m_a ++;
a3.m_a ++;
cout << AA<int>::m_a << endl;
AA<char> b1, b2, b3;
b1.m_a = 'a';
b2.m_a ++;
b2.m_a ++ ;
cout << AA<char>::m_a << endl;
//m_a 應該是 每一種型別的類 使用自己的m_a
cout<<"hello..."<<endl;
system("pause");
return ;
}