C++模板、類模板、函式模板詳解都在這裡了
一、引言
在寫排序演算法時,如果要寫一個交換兩個資料的函式,由於基本資料型別有int、float、double等等型別,所以針對每種資料型別可能我們要寫很多swap函式,這些函式除了傳入的資料型別不同,函式體結構大致相同。所以C++為了避免讓程式設計師寫很多大量重複程式碼,設計了一種叫做“模板”的東西。我們寫程式時,先不指定什麼型別,在呼叫時我們再說明一下是什麼型別,具體怎麼實現接著往下看。
二、函式模板
1、定義
像開頭所說,如果要對int、double型別的兩個數進行交換我們要寫兩個函式,但用函式模板時只需要寫一個函式即可。模板定義如下:
template <typename T>
或者
template <class T>
其中,template是宣告一個模板,typename是宣告一個虛型別T,這個T可以代替int、double等基本資料型別,那為什麼還有class?因為最初是用class宣告一個T來表示通用資料型別,但考慮到class會和“類”混淆,就改用typename進行定義,同時保留了class,這兩者效果相同,但我個人比較習慣用class。
在進行宣告模板之後下面就開始寫模板的函式體了,例如交換函式,合起來就是:
template <class T>
void swap(T *p1,T *p2){
T temp=*p1;
*p1=*p2;
*p2=temp;
}
這樣就是一個完整的函式模板
2、呼叫
有兩種方式使用模板
(1)自動型別推導:編譯器會自動判定你傳入的資料是什麼資料型別,然後將T改成對應資料型別進行操作;
(2)自己宣告資料型別進行呼叫,具體實現如下:
//1、自動型別推導
swap(a, b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
//2、顯示指定型別
swap<int>(a, b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
何時必須自己宣告資料型別呢?
那就是沒有引數傳遞的時候。比如知識一個列印函式,沒有引數傳遞,這裡就不寫程式碼了。
3、多個虛型別
前面是針對一個函式裡面都是同一資料型別的,如果含有不同資料型別,假如有兩個:
template<class T1,class T2>
void func(T1 a,T2 b){....}
1
2
呼叫時可以自動識別型別也可以自己宣告資料型別:
func<int,double>(1,3.14);
1
三、類模板
前面已經介紹了模板大致的作用,這裡對類模板就不做過多說明,直接上乾貨:
1、定義
我們定一個學生類
template<class T1,class T2,class T3>
class Student{
public:
Student(T1 name,T2 age,T3 score){
..........
}
T1 m_Name;
T2 m_Age;
T3 m_Score;
}
2、呼叫
呼叫時必須指定輸入了什麼資料,也就是尖括號不能省略,這點與函式模板不一樣
Student<string,int,float>s("Tom",18,85.5);
1
四、類的函式模板
在類內定義的話就跟前面的函式模板一樣,如果在類外定義,類外也要寫上函式模板的形式:
template<class numType>
class Compare{
public:
Compare(numType a,numType b){
this->a=a;
this->b=b;
}
//宣告模板函式:
numType max( );
private:
numType a;
numTypr b;
}
//類外定義模板函式
template<class numType>
numType Compare::max( ){
return this->a>this->b?this->a:this->b;
}
五、類作為資料型別傳入
//定義Person1
class Person1 {
public:
void showPerson1() {
cout << "Person1 show" << endl;
}
};
//定義Person2
class Person2 {
public:
void showPerson2() {
cout << "Person2 show" << endl;
}
};
//定義類模板
template<class T>
class MyClass {
public:
T obj;
//類模板中的成員函式
void func1() {
obj.showPerson1();
}
void func2() {
obj.showPerson2();
}
};
//主函式
int main() {
MyClass<Person1>m;
m.func1();
//m.func2();//會報錯,因為“showPerson2”: 不是“Person1”的成員
system("pause");
return 0;
}
六、類模板與繼承
如果父類是一個模板,子類繼承父類模板的時候,不知道父類模板記憶體是多少,編譯器就無法給子類分配記憶體,解決方案是給子類也加上模板,方案如下:
template<class T>
class Base {
T m_Name;
};
//class Son :public Base { //錯誤,必須知道T的記憶體才能指定空間
class Son1:public Base<int>{ // 不靈活
};
//想要靈活指定父類中T的型別
template<class T1,class T2>
class Son2 :public Base<T2> {
public:
T1 m_A;
};
int main() {
//讓子類的T為string,子類的T1為int
Son2<int, string>s2;
system("pause");
return 0;
}
七、類模板與友元
前面也有過傳入類物件到函式裡面,如果這個函式需要用到物件裡面的資料,而該資料有被設定為private(私有)就無法直接訪問,此時又要用到友元函式的知識:
注意:類內新增友元函式後,如果沒有對該友元函式進行宣告,編譯器認為你沒有這個函式,會進行報錯。
//先宣告類和函式,防止編譯器報錯
template<class T1,class T2>
class Person;
template<class T1, class T2>
void printPerson(Person<T1, T2> p);
template<class T1,class T2>
class Person {
//全域性函式類外實現的宣告
friend void printPerson<>(Person<T1, T2> p);
public:
Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
//全域性函式類外實現
template<class T1,class T2>
void printPerson(Person<T1, T2> p) {
cout << "Name:" << p.m_Name << endl;
cout << "Age:" << p.m_Age << endl;
}
覺得有用記得頂