1. 程式人生 > >菜鳥學習歷程【32】函式模板與類模板

菜鳥學習歷程【32】函式模板與類模板

函式模板

所謂函式模板,實際上是建立一個通用函式,其函式型別形參型別不具體指定,用一個虛擬的型別來代表。這個通用函式就稱為函式模板。凡是函式體相同的函式都可以用這個模板來代替,不必定義多個函式,只需在模板中定義一次即可。在呼叫函式時系統會根據實參的型別來取代模板中的虛擬型別,從而實現了不同函式的功能。

模板:將 演算法資料型別 相分離;

定義:

template <typename T>
void mySwap(T &a, T &b)
{
    T tmp = a;
    a = b;
    b = tmp;
}

對於上面的函式體,我想大家很熟悉,就是一個交換兩個數的值的過程,但有所不同的是 並沒有明確指明 形參的型別

都將這些型別用 T 來代替。如果將 T 全都替換為 int ,這就是一個實現整型資料交換值的函式,如果將 T 全部替換為 char ,這就是一個實現字元型資料交換值的函式。

那麼,像這樣的函式,我們成為 函式模板

使用規則

1.普通函式呼叫時支援型別的隱式轉換,函式模板呼叫時不支援型別的隱式轉換;
例如:

#include <iostream>

using namespace std;

// 普通函式 在被呼叫時 如果 實參型別 與 形參型別 不一致時 會自動轉換
void print(int a, int b)
{
    cout << "a = "
<< a << ", b = " << b << endl; } int main() { int a = 10, b = 20; char c = 'A'; print(a, b); // a = 10, b = 20 print(a, c); // a = 10, b = 65 return 0; }
#include <iostream>

using namespace std;

// 函式模板 不支援 引數型別的隱式轉換 ,呼叫時 沒有明確 指明哪一種型別 函式模板 不會進行轉換
// 如果呼叫時 明確指明使用 哪一種資料型別 那麼函式模板 會進行資料型別轉化
template<typename T> void print(T a, T b) { cout << "a = " << a << ", b = " << b << endl; } int main() { int a = 10, b = 20; char c = 'A'; print(a, b); print(a, c); // 編譯報錯:沒有找到與引數列表 匹配的函式模板“print”例項 //print<int>(a, c); // 編譯通過,通過<int>告知編譯器 去構造一個 int 型的模板函式 ,那麼在使用時 系統會自動轉換 return 0; }

2.函式模板和普通函式可以共存

1)當 函式模板 和 普通函式 都可以使用時,呼叫 普通函式。如果想呼叫 函式模板 ,可以用空引數列表 <> 進行說明;例如: print<>(a, b);
2)當 函式模板 提供一個更優的選擇,優先呼叫 函式模板;

// 普通函式
void print(char c, int a)
{
    cout<<"a = "<< a <<", c = " << c << endl;
}

// 函式模板
template<typename T>
void print(T c, T a)
{
    cout<<"a = "<< a <<", c = " << c << endl;
}

int main()
{
    int a = 10, b = 20;
    // 普通函式也可以列印 a 與 b 的值,只是會進行隱式的型別轉換
    // 由於 函式模板 的存在,對於 a 與 b 兩個相同型別的 資料,編譯器更願意去 構造一個 int 型別的模板函式 供使用
    print(a, b); 

    return 0;
}

3.函式模板 可以過載

template <typename T>
void print(T a, T b, T c)
{
    cout<<"a = "<< a <<", b = "<< b <<", c = "<< c <<endl;
}

template<typename T>
void print(T a, T b)
{
    cout << "a = " << a << ", b = " << b << endl;
}

函式模板機制

編譯器會對函式模板進行兩次編譯
1.在宣告的地方對模板程式碼本身進行編譯;
2.在呼叫的地方對引數替換後的程式碼進行編譯。

說明:通過 函式模板 產生的函式 被稱為 模板函式

類模板

類模板用於實現類所需資料的型別引數化。

模板類的語法

#include <iostream>

using namespace std;

template <typename T>
class AA
{
public:
    AA()
    {
    }
    AA(T a)
    {
        this->a = a;
    }
    void print ()
    {
        cout << "a = " << a << endl;
    }
private:
    T a;
};

int main()
{
    // 注意:函式模板支援隱式呼叫,類模板不支援隱式呼叫,必須說明模板型別
    AA<int> a(10);
    return 0;
}

類模板物件做函式引數傳遞時
1)寫一個具體的 模板類 物件

template <typename T>
void print(AA<int> &a)
{
    a.print();

template <typename T>
void print(AA<double> &a)
{
    a.print();
}

2)使用函式模板

template <typename T>
void print(AA<T> &a)
{
    a.print();
}

繼承時:
1)派生一個具體的類,繼承自一個具體的模板類

class BB:public AA<int>
{
public:
    BB(int a, int b):AA(a)
    {
        this->b = b;
    }
private:
    int b;
};

2)派生一個 類模板

template <typename T>
class C:public AA<T>
{
public:
    C(int a, T c):AA(a)
    {

    }
private:
    T c;
};

類的實現

以 複數類 (Complex)為例進行說明
1.類的實現放在類的內部

nclude <iostream>

using namespace std;

template <typename T>
class Complex
{

    // 友元函式寫到類的內部
    // 雖然寫到了類的內部,但是該函式還是友元函式,不是類的內部函式
    friend ostream &operator << (ostream &out, Complex &c)
    {
        out << c.a << " + " << c.b << "i";
        return out;
    }

    friend Complex mySub(Complex &c1, Complex &c2)
    {
        Complex tmp(c1.a - c2.a, c1.b - c2.b);
        return tmp;
    }
public:
    Complex(T a = 0, T b = 0)
    {
        this->a = a;
        this->b = b;
    }

    Complex operator +(Complex &c)
    {
        Complex tmp(a + c.a, b + c.b);
        return tmp;
    }
private:
    T a;
    T b;
};

int main()
{
    Complex<int> c1(1, 2), c2(3, 4), c;


    // mySub的實現 雖然是寫到了類的內部,但是它還是一個友元函式
    // 如果是類的內部函式 呼叫如下:  c1.mySub(c2)
    // 但因為 mySub  是友元函式,實際是一個外部的全域性函式,所以只能用如下呼叫:mySub(c1, c2);
    c = mySub(c1, c2);

    cout << c1 << endl;
    cout << c << endl;

    c = c1 + c2;
    cout << c << endl;

    return 0;
}

2.類的實現放在類的外部(同一檔案中)

類的成員函式在類的外部實現時,需要將成員函式寫成函式模板;

#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 ostream &operator<< <T>(ostream &out, Complex<T> &c);

    friend Complex<T> mySub <T>(Complex<T> &c1, Complex<T> &c2);
public:
    Complex(T a = 0, T b = 0);
    Complex operator +(Complex &c);

private:
    T a;
    T b;
};

template <typename T>
Complex<T>::Complex(T a = 0, T b = 0)
{
    this->a = a;
    this->b = b;
}

template <typename T>
Complex<T> Complex<T>::operator +(Complex &c)
{
    Complex tmp(a + c.a, b + c.b);
    return tmp;
}

template <typename T>
ostream & operator << (ostream &out, Complex<T> &c)
{
    out << c.a << " + " << c.b << "i";
    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;
}

int main()
{
    Complex<int> c1(1, 2), c2(3, 4), c;
    c = mySub(c1, c2);

    cout << c1 << endl;
    cout << c << endl;

    c = c1 + c2;
    cout << c << endl;

    return 0;
}

3.類的實現放在類的外部(不同檔案中)

只需要將函式實現“cpp”檔案,改名為“hpp”作為 main.cpp 的標頭檔案即可。

總結:
1)對於在類的內部可 直接實現 的函式,放在類的外部實現時,類中的原型宣告不變;
例如:建構函式、運算子過載(除了 外部 實現的 那 4 種)

template <typename T>
Complex<T>::Complex(T a = 0, T b = 0);        //外部原型
//前面的 Complex<T> 說明是 Complex 的成員函式

Complex(T a = 0, T b = 0);                    //類中宣告

template<typename T>
Complex<T> Complex<T>::operator+(Complex &c); // 外部原型
// 第一個 Complex<T> 是返回值
// 第二個 Complex<T> 說明是Complex 的成員函式

Complex operator+(Complex &c);                // 類中宣告

2.對於只能通過 友元函式 實現的運算子過載函式,在類中宣告時 需要在函式名後,“()”前加上 < T >,以及在類名後加上 < T >;在外部實現時,函式原型 只需要在類名後加上< T >

friend ostream operator << <T>(ostream &out, Complex<T> &obj);
//類中宣告

template <typename T>
ostream operator << (ostream &out, Complex<T> &obj);
// 類外部函式原型

3.對於其他的友元函式(無法在類的內部,改變左值的),在類中宣告時,在函式名後,“()”前加上< T >,以及在類名後 加上 < T >;在類的外部宣告時, 需要在類的上面,對 類 和 函式 進行宣告;

friend Complex<T> mySub<T>(Complex<T> &c1, Complex<T> &c2);
// 類中宣告

template <typename T>   // 宣告類
class Complex;

template <typename T>   // 宣告函式
Complex<T> mySub(Complex<T> &c1, Complex<T> &c2);

template <typename T>   // 外部實現 函式原型
Complex<T> mySub(Complex<T> &c1, Complex<T> &c2);

類模板中的 static 關鍵字

對屬於 同一型別的 模板類的物件 共享 static
對不屬於 同一型別的 模板類的物件 不共享 static

#include <iostream>

using namespace std;

template <typename T>
class A
{
public:
    T a;
    static T sa; 
};

template <typename T>
T A<T>::sa = 1; 

int main()
{
    A<int>  a;
    a.sa = 10;

    A<double> d;
    cout << "sa = " <<d.sa << endl;            //  sa = 1

    cout << "sq = " <<A<int>::sa << endl;      //  sa = 10
    return 0;
}