1. 程式人生 > >C++模板、類模板、函式模板詳解都在這裡了

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;
}

覺得有用記得頂