從例子看C++模版

Michael_Lin發表於2014-10-26

作為現代C++中最具表現力,被應用最廣泛的一個語法功能,Templates(模版),無疑應該被仔細研究。自從有了STL,模版漸漸成為現代C++開發的主角。前不久因為老師的作業要求,我稍微學習了一下C++的模版,寫一篇入門文,請大神輕噴,謝謝。


首先應該看的是函式模版:這是最簡單的模版了。而函式模版中最簡單的例子,應該就是max函式了。以下是我寫的一個example:

    #include <iostream>
    #include <string>
    using namespace std;
    //模版的宣告語法
    //這裡宣告瞭一個tmax模版,其中可能發生變化的只是一個型別T,至於T是什麼我們並不關心
    template <typename T>
    T tmax (T a, T b)
    {
        return a < b ? b : a;
    }
    int main()
    {
        cout << tmax(3,4) << endl;
        cout << tmax(4.9,4.8) << endl;
        string s1("watch"),s2("which");
        cout << tmax(s1,s2) << endl;
        return 0;
    }

模版的魅力就在於,它不需要物件的具體型別,只需要支援進行某些方法或者運算子。這裡的tmax函式,無論拋給它的是int,double還是string,都能正確使用,雖然實際上實現的機理是不一樣的。對於開發者而言,只需要知道物件支援什麼功能即可,並不需要考慮物件究竟是什麼,這很有Duck Typing的味道。需要注意的是,C++對模版的實現機理,實際上是對每種可能的引數情況分別編譯,也就是說,這段程式碼實際上會產生3個tmax,分別對應int,double和string。

這個例子應該很好看懂。接下來我們看類模版:

#include <cstdio>
#include <cstring>
#include <cstdlib>
//宣告一個模版,接收引數分別是型別名T和一個整數N
template <typename T,int N>
class Matrix
{
public:
    T d[N][N];
    T sum()
    {
        T ret=0;
        for (int i=0;i<N;++i)
        {
            for (int j=0;j<N;++j)
            {
                ret+=d[i][j];
            }
        }
        return ret;
    }
    T fill(T x)
    {
    for (int i=0;i<N;++i)
    {
        for (int j=0;j<N;++j)
            {
                d[i][j]=x;
            }
        }
    }
};
Matrix <double, 3> M;
int main()
{
    M.fill(0.1);
    M.d[1][2]+=4.5;
    printf("%lf\n",M.sum());
    return 0;
}

這個例子中,你會發現模版不一定只接收型別,也可以接收一個變數作為模版引數,而這些引數,可以在宣告類的例項的時候使用。至於用法,卻簡單得讓人難以置信。類模版使用的時候需要顯式宣告模版的引數,這大概是類模版和函式模版最大的區別。因為函式模版的用途只是被呼叫,而類模版是用來自動構造一些類的,而構造需要必要的引數。實質的實現和函式模版一樣,類模版也是按需要編譯出一堆東西。我們可以看到,C++模版的本質實際上就是一個複雜而安全的宏語法,它大大擴充套件了C語言裡面簡陋的#define,成為一個安全而高效的工具。

現在我們來看第三個例子,也是C++當中最讓人歎為觀止的例子,這個例子給我們展示了利用模版進行一定程度的函數語言程式設計的方法。

#include <iostream>
using namespace std;

//這裡構造了一個函式物件類Plusa
template <typename T>
class Plusa
    {
    private:
        T a;
public:
    Plusa(T d)
    {
        this->a=d;
    }
    void operator() (T& x)
    {
        x+=a;
    }
};  
/* perform (l, r ,a[], f)
* 對陣列a的l到r施用函式物件f
*/
template <typename T, typename FUNCT>
void perform (int l, int r, T a[], FUNCT f)
{
    for (int i=l;i<r;++i)
    {
        f(a[i]);
    }
}     
int main()
{
    double t[4]={ -0.2, 1.0, 1.2, 1.6 };
    //函式物件plus_half
    Plusa <double> plus_half(0.5);
    perform(0,3,t,plus_half);
    for (int i=0;i<4;++i)
    {
        cout << t[i] <<endl;
    }
    return 0;
}

這個例子中有一個額外的語法:函式物件。如果一個物件,它的()運算子被過載,他就變成了一個函式物件,可以當作函式來呼叫。如果呼叫它,它和函式看上去一模一樣,但事實上它是個物件,所以它可以被輕易地傳遞,而且可以像Javascript的閉包那樣秘密地儲藏一些資訊。

有了函式物件,我們可以再寫一個模版,接受函式物件並使用函式物件,就像例子中的perform函式。函式指標這樣複雜的東西,在C++當中可以輕易地被函式物件取代。基本上STL中所有的有比較功能的模版,全都接收函式物件,足見這個功能被應用的廣泛程度。

以上只是一些簡單的例子,實際上C++的模版遠遠不止這麼點內容,完全去深入的研究,夠寫一本書,而且也有這樣一本書了:《C++ Templates》。最後,希望大家喜歡C++這門優美的程式語言。

相關文章