class
templates
類别模闆
就像上一章所說的 functions 那樣,classes 也可以針對一或多個類型被參數化。用來管理「各種 不同類型的元素」的
container classes(容器類别)就是典型例子。運用 class templates 你可以實 作出可包容各種類型的 container
class。本章将以一個 stack class 作為 class templates
正如你所見,這個 class template 是以标準庫的 class template vector<>
為基礎實作出來的, 這樣我們就可以不考慮記憶體管理、copy構造函數、assignment 運算符等等,進而得以把注意力放在這個 class template
的界面上。
3.1.1
class templates 的聲明
聲明class templates 的動作和聲明function templates
類似:在聲明語句之前加一條述句,以任意辨別符聲明類型參數(type parameters)。我們還是以t 作為辨別符:
相同的規則再次适用:關鍵詞 class
可以代替關鍵詞 typename:
在 class template 内部,t
就像其它任意類型一樣,可用來聲明成員變量(member variables)和成員函數(member functions)。本例之中t
用來聲明vector<> 所容納的元素類型、聲明一個「接受 t const&」的 push()函數、以及聲明一個「傳回類型為 t」的
top()函數:
這個 class 的類型為 stack<t>,t 是一個 template
parameter。現在,無論何時你以這個class聲明變量或函數時,都應該寫成 stack<t>。例如,假設你要聲明自己的 copy
構造函數和assignment 運算符,可以寫為:
然而如果隻是需要class名稱而不是class類型時,隻需寫stack
即可。構造函數和析構函數的聲明就屬于這種情況。
3.1.2
成員函數( member functions)
作為了定義 class template 的成員函數,你必須指出它是個 function template,而且你必須使用
class template 的全稱。是以 stack<t> 的成員函數 push()看起來便像這樣:
這裡調用了vector 的成員函數push_back(),把元素elem 追加到elems
尾端。注意,對一個vector進行 pop_back(),隻是把最後一個元素移除,并不傳回該元素。這種行為乃是基于異常安全性(exception
safety)考慮。實作一個「移除最後元素并傳回,而且完全顧及異常安全性」的 pop() 是不可能的( 這個問題最早由 tom cargill
在[cargillexceptionsafety]中 讨論過),[sutterexceptional] 條款 10
也有讨論)。然而,如果抛開可能的危險,我們可以實作出一個「移除最後元素并傳回」的pop(),但必須聲明一個類型為 t 的區域變量:
當vector為空時,對它進行back()或pop_back()操作會導緻未定義行為。是以我們必須在操作前先檢查stack是否為空。如果stack為空,就抛出一個std::out_of_range
異常。top()之中也需進行相同檢查;該函數傳回 stack 的最後一個元素,但不移除之:
當然,你也可以把任何 class templates 的成員函數實作碼寫在 class
聲明語句中,形成一個 inline函式。例如:
3.2
使用 class template stack
上例聲明了 stack<int>這樣一個類型,表示在該 class template 中 t 被替換為 int。于是
intstack成為這樣一個object:内部使用「可容納int 資料做為元素」的vector,并将被調用之任何成員函數都「以int
類型進行執行個體化」。同樣道理,stack<std::string> 表示:stringstack
被建立為這樣一個object:内部使用「可容納string 資料做為元素」的vector,并将被調用之任何成員函數都「以std::string
類型進行執行個體化」。
注意,惟有被調用到的成員函數,才會被執行個體化(instantiated)。對class templates
而言,隻有當某個成員函數被使用時,才會進行執行個體化。無疑地這麼做可以節省時間和空間。另一個好處是,你甚至可以執行個體化一個class
template,而具現類型并不需要完整支援「class template 内與該
類型有關的所有操作」—前提是那些操作并未真正被叫用。舉個例子,考慮某個class,其某些成員函數使用operator<
對内部元素排序;隻要避免調用這些函數,就可以以一個「并不支援 operator<」的類型來執行個體化這個 class template。
本例中的 default 構造函數、push()函數和 top()函數都同時被 int 和 string
類型加以實作(執行個體化)。然而 pop()隻被 string 執行個體化(譯注:因為 pop()隻被 stringstack 調用)。如果 class
template 擁有 static 成員,這些 static 成員會針對每一種被使用的類型完成執行個體化。
你可以像面對任何基本類型一樣地使用一個執行個體化後的class template類型,前提是你的使用方式合法:
運用 typedef,你可以更友善地使用 class
templates:
注意,在c++中,typedef 并不産生新類型,隻是為既有類型産生一個别名(type
alias)。是以
在以下述句之後: