1. 程式人生 > >33、不一樣的C++系列--類模板與特化

33、不一樣的C++系列--類模板與特化

類模板

類模組的概念和意義

在C++中有這樣一些類:

  • 主要用於儲存和組織資料元素
  • 類中資料組織的方式和資料元素的具體型別無關
  • 如:陣列類,連結串列類,Stack類,Queue類等

C++中將模板的思想應用於類,使得類的實現不關注資料元素的具體型別,而只關注類所需要實現的功能。

所以C++中的類模板是這樣的:

  • 以相同的方式處理不同型別的資料
  • 在類宣告前使用template進行標識
  • < typename T >用於說明類中使用的泛指型別 T
  • 語法:
template<typename T>
class Operator
{
public:
    T op(T a, T b)
; }
;

類模板的應用:

  • 只能顯示指定具體型別,無法自動推導
  • 使用具體型別 < Type > 定義物件
  • 用法:
Operator <int> op1;
Operator <string> op2;
int i = op1.op(1, 2);
string s = op2.op("D.T.", "Software");

類模板的進一步理解:

  • 宣告的泛指型別 T 可以出現在類模板的任意地方
  • 編譯器對類模板的處理方式和函式模板相同
    • 從類模板通過具體型別產生不同的類
    • 在宣告的地方對類模板程式碼本身進行編譯
    • 在使用的地方對引數替換後的程式碼進行編譯

這裡舉一個例子:

#include <iostream>
#include <string>

using namespace std;

//過載string類減號型別操作符
string operator-(string& l, string& r)
{
    return "Minus";
}

//定義一個類模板
//在類模板中有4個操作
template < typename T >
class Operator
{
public:
    T add(T a, T b)
    {
        return a + b;
    }
    T minus(T a, T b)
    {
        return
a - b; } T multiply(T a, T b) { return a * b; } T divide(T a, T b) { return a / b; } }; int main() { //使用類模板建立一個物件,型別為int Operator<int> op1; //物件呼叫類的成員函式 cout << op1.add(1, 2) << endl; //使用類模組建立一個物件,型別為string Operator<string> op2; //物件呼叫類的成員函式 cout << op2.add("D.T.", "Software") << endl; cout << op2.minus("D.T", "Software") << endl; return 0; }

輸出結果為:

3
D.T.Software
Minus

類模板在工程中是怎麼使用的呢?

  • 類模組必須在標頭檔案中定義
  • 類模組不能分開實現在不同的檔案中
  • 類模組外部定義的成員函式需要加上模板 < > 宣告

這裡做一個示例:

在標頭檔案 Operator.h 中:

#ifndef _OPERATOR_H_
#define _OPERATOR_H_

//宣告類模組
template < typename T >
class Operator
{
public:
    T add(T a, T b);
    T minus(T a, T b);
    T multiply(T a, T b);
    T divide(T a, T b);
};
//實現類模組中各個成員函式的邏輯
template < typename T >
T Operator<T>::add(T a, T b)
{
    return a + b;
}
//實現類模組中各個成員函式的邏輯
template < typename T >
T Operator<T>::minus(T a, T b)
{
    return a - b;
}
//實現類模組中各個成員函式的邏輯
template < typename T >
T Operator<T>::multiply(T a, T b)
{
    return a * b;
}
//實現類模組中各個成員函式的邏輯
template < typename T >
T Operator<T>::divide(T a, T b)
{
    return a / b;
}

#endif

呼叫使用時:

#include <iostream>
#include <string>
#include "Operator.h"

using namespace std;

int main()
{
    //使用類模組建立類物件
    Operator<int> op1;

    //類物件使用各個成員函式
    cout << op1.add(1, 2) << endl;
    cout << op1.multiply(4, 5) << endl;
    cout << op1.minus(5, 6) << endl;
    cout << op1.divide(10, 5) << endl;

    return 0;
}

輸出結果為:

3
20
-1
2

多引數類模組

在類模組中可以定義任意多個不同的型別引數,比如這樣:

template 
<typename T1, typename T2>
class Test
{
public:
    void add(T1 a, T2 b);
};

//使用
Test<int, float> t;

類模組的特化

這裡再學習一個類模組的知識,就是它可以被 特化

  • 指定類模組的特定實現
  • 部分型別引數必須顯示指定
  • 根據型別引數分開實現類模組
  • 語法:
template
<typename T1, typename T2>
class Test
{
};

//特化
template
<typename T>
class Test <T, T>
{
};

類模組可以被 特化 ,當然對於特化還有特化型別:

  • 部分特化 — 用特定規則約束型別引數
  • 完全特化 — 完全顯示指定型別引數
template
<typename T1, typename T2>
class Test
{
};

//完全特化
template
<  >
class Test < int, int >
{
};

這裡舉一個例子:

#include <iostream>
#include <string>

using namespace std;

template
< typename T1, typename T2 >
//正常的類模組
class Test
{
public:
    void add(T1 a, T2 b)
    {
        cout << "void add(T1 a, T2 b)" << endl;
        cout << a + b << endl;
    }
};

template
< typename T1, typename T2 >
// 關於指標的特化實現
//部分特化:用指標型別約束型別引數
class Test < T1*, T2* >
{
public:
    void add(T1* a, T2* b)
    {
        cout << "void add(T1* a, T2* b)" << endl;
        cout << *a + *b << endl;
    }
};

template
< typename T >
// 當 Test 類模板的兩個型別引數完全相同時,使用這個實現
//部分特化:用引數型別完全相等的規則約束
class Test < T, T >
{
public:
    void add(T a, T b)
    {
        cout << "void add(T a, T b)" << endl;
        cout << a + b << endl;
    }
    void print()
    {
        cout << "class Test < T, T >" << endl;
    }
};

template
<  >
// 當 T1 == void* 並且 T2 == void* 時
//完全特化 完全顯示指定型別引數
class Test < void*, void* >
{
public:
    void add(void* a, void* b)
    {
        cout << "void add(void* a, void* b)" << endl;
        cout << "Error to add void* param..." << endl;
    }
};

int main()
{  
    //2個型別不同,呼叫普通類模組
    Test<int, float> t1;
    //2個型別相同,呼叫用引數型別完全相等的規則約束的類模組
    Test<long, long> t2;
    //2個型別完全相等,並且符合已經指定引數型別的類模板
    Test<void*, void*> t3;

    t1.add(1, 2.5);

    t2.add(5, 5);
    t2.print();

    t3.add(NULL, NULL);
    //2個引數都為指標,且型別不同
    //呼叫用指標型別約束型別引數的類模板
    Test<int*, double*> t4;
    int a = 1;
    double b = 0.1;

    t4.add(&a, &b);

    return 0;
}

輸出結果為:

void add(T1 a, T2 b)
3.5
void add(T a, T b)
10
class Test < T, T >
void add(void* a, void* b)
Error to add void* param...
void add(T1* a, T2* b)
1.1

使用類模組特化也要注意一些地方:

  • 特化只是模組的分開實現
    • 本質上是同一個類模組
  • 特化類模板的使用方式是統一的
    • 必須顯示指定每一個型別引數

類模組特化的進一步理解

其實有沒有發現特化和重定義有點相似,但也有些區別:

  • 重定義
    • 一個類模組和一個新類(或者兩個類模組)
    • 使用的時候需要考慮如何選擇的問題
  • 特化
    • 以統一的方式使用類模組和特化類
    • 編譯器自動優先選擇特化類

那既然類模組可以特化,函式模組可不可以特化呢?

  • 函式模板只支援型別引數完全特化
  • 使用方法:
template
<typename T>
//函式模組定義
bool Equal(T a, T b)
{
    return a == b;
}

template
< >
//函式模組完全特化
bool Equal<void *>(void* a, void* b)
{
    return a == b;
}

這裡舉一個例子:

#include <iostream>
#include <string>

using namespace std;

//普通函式模組
template
< typename T >
bool Equal(T a, T b)
{
    cout << "bool Equal(T a, T b)" << endl;

    return a == b;
}

//完全特化後的函式模組
template
< >
bool Equal<double>(double a, double b)
{
    const double delta = 0.00000000000001;
    double r = a - b;

    cout << "bool Equal<double>(double a, double b)" << endl;

    return (-delta < r) && (r < delta);
}

//普通函式
bool Equal(double a, double b)
{
    const double delta = 0.00000000000001;
    double r = a - b;

    cout << "bool Equal(double a, double b)" << endl;

    return (-delta < r) && (r < delta);
}

int main()
{  
    //呼叫函式
    cout << Equal( 1, 1 ) << endl;
    //呼叫完全特化後的函式模組
    cout << Equal<>( 0.001, 0.001 ) << endl;

    return 0;
}

輸出結果為:

bool Equal(T a, T b)
1
bool Equal<double>(double a, double b)
1

這裡要注意:

當需要過載函式模組時,優先考慮使用模板特化;當模板特化無法滿足需求,再使用函式過載!

小結

  • 泛型程式設計的思想應用於類的形式就是類模組
  • 類模板以相同的方式處理不同型別的資料
  • 類模組非常適用於編寫資料結構相關的程式碼
  • 類模組在使用時只能顯示指定型別
  • 類模組可以定義任意多個不同的型別引數
  • 類模組可以被部分特化和完全特化
  • 特化的本質是模板的分開實現
  • 函式模板只支援完全特化
  • 工程中使用模板特化代替類(函式)重定義