2014年3月26日 星期三

類別範本(Class template)

http://openhome.cc/Gossip/CppGossip/ClassTemplate.html

可以在C++中使用「類別範本」(Class template),又稱「通用類別」(Generic class),舉個例子來說,曾經介紹過如何撰寫一個自訂的陣列類別(以 自訂例外類別 中的SafeArray為例),當時該類別很明顯的,只能用於建立int型態的陣列,如果您想要使用於double、char或其它資料型態,難道要一一 建立這些類別嗎?當然不是,您可以宣告類別為類別範本,讓我們的程式碼撰寫可以提高重覆使用。

要宣告一個類別成為樣版類別,您可以這麼宣告: 

template <class 型態名稱> 
class 類別名稱{ 
    // ........ 
};

在類別定義外部定義的樣版函式(在::之前加上<型態名稱>),以及如何使用樣版類別建立物件,這邊直接使用實例來介紹:

  • SafeArray.h
#include "ArrayIndexOutOfBoundsException.h"

template <class CType>
class SafeArray { 
public: 
    // 建構函式 
    SafeArray(int); 
    // 解構函式 
    ~SafeArray();
 
    CType get(int); 
    void set(int, CType);
 
    int length;
private:
    CType *_array; 

    bool isSafe(int i); 
};

// 動態配置陣列
template <class CType>
SafeArray<CType>::SafeArray(int len) {
    length = len;
    _array = new CType[length];
}

// 測試是否超出陣列長度
template <class CType>
bool SafeArray<CType>::isSafe(int i) {
    // 
    if(i >= length || i < 0) {
        return false;
    } 
    else {
        return true;
    }
}

// 取得陣列元素值
template <class CType>
CType SafeArray<CType>::get(int i) {
    if(isSafe(i)) {
        return _array[i]; 
    }
    else {
        // 存取超過陣列長度,丟出例外 
        throw ArrayIndexOutOfBoundsException(i); 
    }
}

// 設定陣列元素值
template <class CType>
void SafeArray<CType>::set(int i, CType value) {
    if(isSafe(i)) {
        _array[i] = value;
    }
    else {
        // 存取超過陣列長度,丟出例外
        throw ArrayIndexOutOfBoundsException(i); 
    }
}

// 刪除動態配置的資源
template <class CType>
SafeArray<CType>::~SafeArray() {
    delete [] _array;
}

您還需要自訂例外類別 中的Exception與ArrayIndexOutOfBoundsException類別,一個使用SafeArray類別範本的例子如下:
  • main.cpp
#include <iostream> 
#include "SafeArray.h"
#include "Exception.h"
#include "ArrayIndexOutOfBoundsException.h"
using namespace std; 

int main() {
    SafeArray<int> safeArray1(10);
    SafeArray<double> safeArray2(10);
 
    try {
        for(int i = 0; i < safeArray1.length; i++) {
            safeArray1.set(i, (i + 1) * 10);
        }
 
        for(int i = 0; i < safeArray1.length; i++) {
            cout << safeArray1.get(i) << " ";
        }

        cout << endl;

        for(int i = 0; i < safeArray2.length; i++) {
            safeArray2.set(i, (i + 1) * 0.1);
        }
 
        for(int i = 0; i < safeArray2.length; i++) {
            cout << safeArray2.get(i) << " ";
        }
 
        cout << endl;
    }
    catch(ArrayIndexOutOfBoundsException e) {
        cout << endl
             << e.message()
             << endl;
    }
    catch(Exception e) {
        cout << endl
             << e.message()
             << endl;
    }
 
    return 0; 
} 

執行結果:

10 20 30 40 50 60 70 80 90 100
0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1


在這個例子中,您可以分別建立一個物件內部成員為int與double型態的SafeArray實例,但不用為它們個別撰寫不同的類別,這可以讓我們所定 義的類別更具有通用性。

函式範本 (Function template)

http://openhome.cc/Gossip/CppGossip/FunctionTemplate.html

來看看氣泡排序法:
// 整數氣泡排序法
void bubbleSort(int number[], int length) { 
    int i, j, k, flag = 1; 
    int tmp; 

    for(i = 0; i < length-1 && flag == 1; i++) { 
        flag = 0; 
        for(j = 0; j < length-i-1; j++) { 
            if(number[j+1] < number[j]) { 
                tmp = number[j]; 
                number[j] = number[j+1]; 
                number[j+1] = tmp; 
                flag = 1; 
            } 
        } 
    } 


// 浮點數氣泡排序
void bubbleSort(float number[], int length) { 
    int i, j, k, flag = 1; 
    float tmp; 

    for(i = 0; i < length-1 && flag == 1; i++) { 
        flag = 0; 
        for(j = 0; j < length-i-1; j++) { 
            if(number[j+1] < number[j]) { 
                tmp = number[j]; 
                number[j] = number[j+1]; 
                number[j+1] = tmp; 
                flag = 1; 
            } 
        } 
    } 
}

函式範本(Function template)又稱「通用函式」(Generic function),它可以適用於不同資料型態的參數列,但實作內容相同的函式,以上面的氣泡排序法為例,除了參數列資料型態不同之外,其實排序的程式碼 幾乎相同,實在沒必要為了資料型態的不同而重覆撰寫相同的程式碼。

建立函式範本的方法之一是使用"template"關鍵字,並使用class來宣告取代用的資料型態宣告字,使用下面這個程式作個示範:
 

#include <iostream> 
using namespace std; 

template <class X> 
void bubbleSort(X[], int); 

int main() { 
    int iarr[] = {5, 3, 11, 6, 8, 9, 20, 11}; 
    float farr[] = {5.1, 3.4, 11.3, 6.6, 8.9, 9.11, 20.2, 11.4}; 

    bubbleSort(iarr, 8); 
    for(int i = 0; i < 8; i++) 
        cout << " " << iarr[i]; 
    cout << endl; 

    bubbleSort(farr, 8); 
    for(int i = 0; i < 8; i++) 
        cout << " " << farr[i]; 
    cout << endl; 
 
    return 0; 
} 

template <class X> 
void bubbleSort(X number[], int length) { 
    int i, j, k, flag = 1; 
    X tmp; 

    for(i = 0; i < length-1 && flag == 1; i++) { 
        flag = 0; 
        for(j = 0; j < length-i-1; j++) { 
            if(number[j+1] < number[j]) { 
                tmp = number[j]; 
                number[j] = number[j+1]; 
                number[j+1] = tmp; 
                flag = 1; 
            } 
        } 
    } 
}

執行結果:

  3 5 6 8 9 11 11 20
 3.4 5.1 6.6 8.9 9.11 11.3 11.4 20.2


在這個程式中,編譯器會自動產生兩個版本的bubbleSort()函式,一個是整數陣列的參數,一個是浮點數陣列的參數,也可以使用 "typename"來取代class關鍵字,例如: 
template <typename X> 
void bubbleSort(X number[], int length) { 
    .... 
}

"typename"是後來才加入至C++中的,早期的撰寫風格主要都是使用"class"。

在宣告了函式範本之後,您還是可以自行重載函式,例如: 

template <class X> 
void bubbleSort(X number[], int length) { 
.... 


void bubbleSort(int number[], int length, int descend) { 
// 以descend決定要由大至小或由小至大排序 
.... 
}

在這個程式片段中,雖然您宣告了樣版函式bubbSort(),但您仍自行重載了一個函式以自行定義特定的函式功能。

【Chrome外掛】Chrome YouTube Downloader 直接下載Youtube影片&轉mp3下載


http://www.mandel-design.xf.cz/Chrome-Youtube-Downloader/