template 雖然很重要,但它與「運用mfc」有什麼關係?有!第8章當我們開始設計scribble 程式時,需要用到mfc 的collection classes,而這一組類別自從mfc 3.0以來就有了template 版本(因為visual c++ 編譯器從2.0 版開始支援c++ template)。運用之前,我們總該了解一下新的語法、精神、以及應用。
無性生殖並不只是存在於遺傳工程上,對程式設計師而言它也是乙個由來已久的動作。過去,我們只不過是以乙個簡單而基本的工具,也就是乙個文字編輯器,重制我們的程式**。今天,c++ 提供給我們乙個更好的繁殖方法template。複製一段既有程式**的乙個最平常的理由就是為了改變資料型別。舉個例子,假設你寫了乙個繪圖函式,使用整數x, y 座標;突然之間你需要相同的程式**,但座標值改採用long。你當然可以使用乙個文字編輯器把這段碼拷貝乙份,然後把其中的資料型別改變過來。有了c++,你甚至可以使用多載(overloaded)函式,那麼你就可以仍舊使用相同的函式名稱。函式的多載的確使我們有比較清爽的程式**,但它們意味著你還是必須
在你的程式的許多地方維護完全相同的演算法。c 語言對此問題的解答是:使用巨集。雖然你因此對於相同的演算法只需寫一次程式**,但巨集有它自己的缺點。第一,它只適用於簡單的功能。第二個缺點比較嚴重:巨集不提供資料型別檢驗,因此犧牲了c++ 的乙個主要效益。第三個缺點是:巨集並非函式,程式中任何呼叫巨集的地方都會被編譯器前置處理器原原本本地插入巨集所定義的那一段碼,而非只是乙個函式呼叫,因此你每使用一次巨集,你的執行檔案就會膨脹一點。templates 提供比較好的解決方案,它把「一般性的演算法」和其「對資料型別的實作部份」區分開來。你可以先寫演算法的程式**,稍後在使用時再填入實際資料型別。新的c++ 語法使「資料型別」也以引數的姿態出現。有了template,你可以擁有巨集「只寫一次」的優點,以及多載函式「型別檢驗」的優點。
c++ 的template 有兩種,一種針對function,另一種針對class。
template functions
template
t power(t base, int exponent);
0001 template
0002 t power(t base, int exponent)
0003
主義:上面就是power 函式的template 版本:傳回值必須確保為型別t,以吻合template 函式的宣告。
下面是template 函式的呼叫方法
#0001 #include
#0002 void main()
#0003
執行結果如下:
i= 625
l= 1000000000
d= 1e+010
在第一次呼叫中,t 變成int,在第二次呼叫中,t 變成long。而在第三次呼叫中,t 又成為了乙個long double。但如果呼叫時候把資料型別混亂掉了,像這樣:int i = power(1000l, 4); // 基值是個long,傳回值卻是個int。錯誤示範!編譯時就會出錯。
template 函式的資料型別引數t 究竟可以適應多少種型別?我要說,幾乎「任何資料型態」都可以,但函式中對該型別數值的任何運算動作,都必須支援-- 否則編譯器就不知道該怎麼辦了。以power 函式為例,它對於result 和base 兩個數值的運算動作有:
1. t result = base;
2. return (t)1;
3. return (t)0;
4. result *= base;
5. return result;
c++ 所有內建資料型別如int 或long 都支援上述運算動作。但如果你為某個c++ 類別產生乙個power 函式,那麼這個c++ 類別必須包含適當的成員函式以支援上述動作。如果你打算在template 函式中以c++ 類別代替class t,你必須清楚知道哪些運算動作曾被使用於此一函式中,然後在你的c++ 類別中把它們全部實作出來。否則,出現的錯誤耐人尋味
template classes
我們也可以建立template classes,使它們能夠神奇地操作任何型別的資料。下面這個例子是讓cthree 類別儲存三個成員變數,成員函式min 傳回其中的最小值,成員函式max 則傳回其中的最大值。我們把它設計為template class,以便這個類別能適用於各式各樣的資料型別:
#0001 template
#0002 class cthree
#0003 ;
#0001 template
#0002 t cthree::min()
#0003
#0007
#0008 template
#0009 t cthree::max()
#0010
#0014
#0015 template
#0016 cthree::cthree(t t1, t t2, t t3) :
#0017 a(t1), b(t2), c(t3)
#0018
這裡就得多注意些了。每乙個成員函式前都要加上template ,而且類別名稱應
該使用cthree。
以下是template class 的使用方式:
#0001 #include
#0002 void main()
#0003
執行結果如下:
2 5-6.75
8.52
364873
646600
稍早我曾說過,只有當template 函式對於資料型別t 支援所有必要的運算動作時,t 才得被視為有效。此一限制對於template classes 亦屬實。為了針對某些類別產生乙個cthree,該類別必須提供copy 構造式以及operator
但是如果你用的是別人template classes,你又如何知道什麼樣的運算動作是必須的呢?唔,該template classes 的說明檔案中應該有所說明。如果沒有,只有源**才能揭露秘密。c++ 內建資料型別如int 和float 等不需要在意這份要求,因為所有內建的資料型別都支援所有的標準運算動作。
templates 的編譯與聯結
對程式設計師而言c++ templates 可說是十分容易設計與使用,但對於編譯器和聯結器而言卻是一大挑戰。編譯器遇到乙個template 時,不能夠立刻為它產生機器碼,它必須等待,直到template 被指定某種型別。從程式設計師的觀點來看,這意味著template function 或template class 的完整定義將出現在template 被使用的每乙個角落,否則,編譯器就沒有足夠的資訊可以幫助產生目標程式碼。當多個原始檔使用同乙個template 時,事情更趨複雜。隨著編譯器的不同,掌握這種複雜度的技術也不同。有乙個常用的技術,borland 稱之為**art,應該算是最容易的:每乙個使用template 的程式**的目的檔中都存在有template碼,聯結器負責複製和刪除。假設我們有乙個程式,包含兩個原始檔a.cpp 和b.cpp,以及乙個three.h(其內定義了乙個template 類別,名為cthree)。a.cpp 和b.cpp 都包含three.h。如果a.cpp以int 和double 使用這個template 類別,編譯器將在a.obj 中產生int 和double 兩種版本的template 類別可執行碼。如果b.cpp 以int 和float 使用這個template 類別,編譯器將在b.obj 中產生int 和float 兩種版本的template 類別可執行碼。即使雖然a.obj 中已經有乙個int 版了,編譯器沒有辦法知道。然後,在聯結過程中,所有重複的部份將被刪除。請看下圖。
附氣泡排序的模板
template
void bubble(stype *item,int count)}}
}void main()
;bubble(num,6);
for (int i=0;i<6;i++)
}
c 重要特性之一委託
委託的構成必須滿足的4個條件 宣告委託型別 必須有乙個方法包含了要執行的 必須建立乙個委託例項 必須呼叫 invoke 委託例項 委託包裝的方法需要滿足以下條件 示例一 using system using system.collections.generic using system.linq u...
C 11的模板新特性 變長引數的模板
這個特性很讚,直接給例子吧,假如我要設計乙個類,cachedfetcher內部可能使用std map也可能使用std unordered map,也可能是其它的map,怎麼設計呢?沒有c 11變長模板之前這很費勁。因為map的模板引數可不是只有key,value兩個啊,還有一些有預設引數的templ...
模板是C 的乙個特性
模板是c 的乙個特性,是函式和類可以作用於不同的型別上而不需要針對每乙個具體型別重複相同的 與模板相反,我們已經學過的過載 overloading 對過載函式而言,c 的檢查機制能通過函式引數的不同及所屬類的不同。正確的呼叫過載函式。例如,為求兩個數的最大值,我們定義max 函式需要對不同的資料型別...