c++ 模板 c++ 模板

 
在c++中有如下語句
int a;
char b;
long c;
float d;
像上面的 int, char, long, float 被稱爲“類型”。
有時需要將“類型”也做爲參數來處理,比如,要寫一個比較大小的函數 comp(x,y)
如果是兩個int型比較就返回一個int類型的值,如果是兩個float型比較就返加一個float型結果,當然可以用函數重載來實現,但利用函數模板就只要寫一個函數
template<class T>
T comp(T x,T y)
{
return x>y?x:y;
}
其中template<class T>就是聲明將T作爲一個類型參數 和以下重載的函數比較一下
int comp(int x,int y)
{
return x>y?x:y;
}
float comp(float x,float y)
{
return x>y?x:y;
}
簡單的說, 模板 就是一個函數或類模板可以解決不同類型的問題


#include "stdafx.h"
#include<iostream>
#include <conio.h>
#include<string.h>
using namespace std;
template <class T>
T s(T i,T j)
{
T a;
a=i;
i=j;
j=a;
return a ;
}

int _tmain(int argc, _TCHAR* argv[])
{

int i=1,j=2;
cout<<i<<"-"<<j<<endl;
s(i,j);
cout<<i<<"-"<<j<<endl;
char c='a',h='b';
cout<<c<<"-"<<h<<endl;
s(c,h);
cout<<c<<"-"<<h<<endl;

return 0;
}

////////////////////////////////////////////////////////////////

C++ 模板基礎談

1. 什麼是模板

模板定義:模板就是實現代碼重用機制的一種工具,它可以實現類型參數化,即把類型定義爲參數,從而實現了真正的代碼可重用性。

我們知道,C++ 是一種“強類型”的語言,也就是說一個變量,編譯器必須確切的知道它的類型,而模板就是構建在這個強類型語言基礎上的泛型系統。

2. 模板的語法

模板函數

template < typename {類型參數名稱}, [ int {Name}=...][, ...] >

{函數定義}

模板類

template < typename ... , [ int {Name}=...] >

class ...

模板的參數可以是類型,或者是一個 int 型的值(或者可以轉換爲int 型的,比如 bool)。

3. 模板的使用

顯式類型參數:對於模板函數,在函數名後添加 < {類型參數表} >。對於模板類,在類後添加 < {類型參數表} >

隱式類型參數:對於模板函數,如果類型參數可以推導,那麼可以省略類型參數表

舉個例子:

template < typename T >

T max( T a, T b )

{

return a < b ? b : a;

}

這個 max 函數就是一個模板函數,它可以傳入一個 “類型”的參數,以便實現任意類型求最大值的效果。假設我們這樣使用它:

int x=5, y=10;

int z=max <int>( x, y );

這時候發生了什麼呢?我們傳入的“類型參數”是int,因此編譯器在編譯這段代碼時會使用 int 來構造一個新函數:

int max( int a, int b )

{

return a < b ? b : a;

}

後面的事就和編譯普通的函數一樣了,C++編譯器繼續使用強類型系統編譯這個函數,由強類型系統來檢查這個函數是否正確。

這個過程叫做模板的“特化”,它發生在編譯期,當編譯器發現模板函數、模板類被使用(注意,不是定義)的時候進行的。這個系統實際上比較像宏,但是比宏更爲智能。

很明顯,編譯器必須知道模板如何特化這個函數,因此模板函數的實現,必須在“使用點”之前,因此模板庫只能通過頭文件庫的形式來提供。

4. 模板的類型推導

對於函數,編譯器是知道傳入參數的類型的,比如上面的max,max < ? >( x, y ),由於第一個參數 x 是 int 類型的,那麼 ? 這裏需要填寫什麼呢?

我們可以很明顯的推斷出應該是 "int",否則,後面的強類型系統將無法編譯這個函數。編譯器同樣知道 x 的類型,因此它也能推導出“類型參數”,這時候我們調用時就可省略模板參數了。

這個推導是按順序來的,因此如果上面的 y 是其他類型,? 仍然會被推導爲 int,如果y無法隱性轉換爲int,強類型編譯時就會報錯。

5. 類型推導的隱式類型轉換

在決定模板參數類型前,編譯器執行下列隱式類型轉換:

左值變換

修飾字轉換

派生類到基類的轉換

見《C++ Primer》([注2],P500)對此主題的完備討論。

簡而言之,編譯器削弱了某些類型屬性,例如我們例子中的引用類型的左值屬性。舉例來說,編譯器用值類型實例化函數模板,而不是用相應的引用類型。

同樣地,它用指針類型實例化函數模板,而不是相應的數組類型。

它去除const修飾,絕不會用const類型實例化函數模板,總是用相應的非 const類型,不過對於指針來說,指針和 const 指針是不同的類型。

底線是:自動模板參數推導包含類型轉換,並且在編譯器自動決定模板參數時某些類型屬性將丟失。這些類型屬性可以在使用顯式函數模板參數申明時得以保留。

6. 模板的偏特化

如果我們打算給模板函數(類)的某個特定類型寫一個函數,就需要用到模板的偏特化,比如我們打算用 long 類型調用 max 的時候,返回小的值(原諒我舉了不恰當的例子):

template <> // 這代表了下面是一個模板函數

long max <long>( long a, long b ) // 對於 vc 來說,這裏的 <long> 是可以省略的

{

return a > b ? b : a;

}

實際上,所謂偏特化,就是代替編譯器完成了對指定類型的特化工作,現代的模板庫中,大量的使用了這個技巧。

7. 仿函數

仿函數這個詞經常會出現在模板庫裏(比如 STL),那麼什麼是仿函數呢?

顧名思義:仿函數就是能像函數一樣工作的東西,請原諒我用東西這樣一個代詞,下面我會慢慢解釋。

void dosome( int i )

這個 dosome 是一個函數,我們可以這樣來使用它: dosome(5);

那麼,有什麼東西可以像這樣工作麼?

答案1:重載了 () 操作符的對象,比如:

struct DoSome

{

void operator()( int i );

}

DoSome dosome;

這裏類(對 C++ 來說,struct 和類是相同的) 重載了 () 操作符,因此它的實例 dosome 可以這樣用 dosome(5); 和上面的函數調用一模一樣,不是麼?所以 dosome 就是一個仿函數了。

實際上還有答案2:

函數指針指向的對象。

typedef void( *DoSomePtr )( int );

typedef void( DoSome )( int );

DoSomePtr *ptr=&func;

DoSome& dosome=*ptr;

dosome(5); // 這裏又和函數調用一模一樣了。

當然,答案3 成員函數指針指向的成員函數就是意料之中的答案了。

8. 仿函數的用處

不管是對象還是函數指針等等,它們都是可以被作爲參數傳遞,或者被作爲變量保存的。因此我們就可以把一個仿函數傳遞給一個函數,由這個函數根據需要來調用這個仿函數(有點類似回調)。

STL 模板庫中,大量使用了這種技巧,來實現庫的“靈活”。

比如:

for_each, 它的源代碼大致如下:

template < typename Iterator, typename Functor >

void for_each( Iterator begin, Iterator end, Fucntor func )

{

for( ; begin!=end; begin++ )

func( *begin );

}

這個 for 循環遍歷了容器中的每一個元素,對每個元素調用了仿函數 func,這樣就實現了 對“每個元素做同樣的事”這樣一種編程的思想。

特別的,如果仿函數是一個對象,這個對象是可以有成員變量的,這就讓 仿函數有了“狀態”,從而實現了更高的靈活性。

我的一點點理解:關於類之間的模板值傳遞

先貼一段程序,vs2008,編譯通過:

#include <iostream>

using namespace std;

template <class T>

class classOne{

private:

T a;

public:

T getA(T b){

a=b;

return a;

}

};

template <class T>

class classTwo{

private:

classOne<T> test;

public:

T out(T b){

return test.getA(b);

}

};

int main(){

classTwo<int> test2;

cout<<test2.out(3)<<endl;

return 0;

}

將classOne定義爲模板類,我原來一直搞不清楚classTwo如何傳遞模板值給它,其實和一般傳值是一樣的了,但似乎網上的教程都沒有說,或許是太簡單了,只是我理解不透沒想到。恩,不作解釋了,是在看不懂的,留言回答

當然這裏還有一個弱點,就是在訪問classTwo中訪問classOne實例的a屬性時,由於是私有屬性,所以在classTwo中不能直接test.a,訪問,解決這個問題,用友元類。

#include <iostream>

using namespace std;

template <class T>

class classOne{

friend classTwo<T>;

private:

T a;

public:

T getA(T b){

a=b;

return a;

}

};

template <class T>

class classTwo{

private:

classOne<T> test;

public:

T out(T b){

test.a=b;

return test.a;

}

};

int main(){

classTwo<int> test2;

cout<<test2.out(3)<<endl;

return 0;

}

但還是報錯。原因在於先聲明友元類。在classOne上面如此聲明就可以了。

template <class T>

class classTwo;

模板stack 堆棧實現:http://hi.baidu.com/%B4%E5%E6%DE/blog/item/44318981e11ebbde9123d9e5.html

模板queue循環隊列現:http://hi.baidu.com/%B4%E5%E6%DE/blog/item/bf79efaebdf814c57cd92ae4.html

模板 單鏈表 循環鏈表 雙向鏈表 實現:http://hi.baidu.com/%B4%E5%E6%DE/blog/item/204876fbc421ef809f514622.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章