#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