D 語言入門/模板和泛型程式設計/模板類
外觀
容器類通常在能夠容納任何型別的項時最有用。否則,你將不得不為想要容納的每種型別編寫不同的容器。傳統的面向物件方法(在 Java 1.5 版本之前使用)利用了多型性。由於所有類都從Object派生,因此容納Object例項的容器可以充當通用容器。這有許多缺點
- 原始型別(例如int)不能直接放入容器中。你必須編寫一個包裝類(比如 Java 的Integer)來容納此類項。
- 容器不是型別安全的。你必須從Object轉換為它們的實際型別,然後才能從容器中刪除它們。此外,容器中項的實際型別沒有特別限制,這意味著給定容器能夠容納各種型別的隨機組合。
- 所有這些向上和向下轉換都會對效能產生負面影響。
為想要容納的每種型別編寫自定義容器類的簡單解決方案有明顯的缺點。(最明顯的是,它不鼓勵程式碼重用。)
模板類最初是新增到 C++ 中來解決這個問題的,而 D 的模板功能是基於 C++ 中的模板功能。庫編寫者首先編寫一個泛型容器(比如,連結串列),就好像任何型別都可以包含在其中一樣。在容器的開頭,庫編寫者會放置一個佔位符來表示將儲存在其中的型別。按照慣例,我們通常將其稱為 T。這個泛型類被稱為模板類,之所以這樣稱呼是因為它充當了更具體實現的模板。
D 中的模板類看起來像這樣
class Foo(T)
{
}
注意(T)在類名之後。這是模板引數列表。要實際使用該類,我們必須透過以下方式對其進行例項化
auto f = new Foo!(int);
模板引數列表可以像函式引數一樣包含多個引數。僅包含名稱的引數被認為是型別。嘗試傳遞除型別以外的任何內容到T將導致錯誤。模板引數有四種類型
- 型別
- 上面的示例中的TT
- 是一個型別引數。
- 常量值幾乎任何可以在編譯時確定的值都可以用作模板引數。值模板引數看起來與函式引數相同,例如int i在模板引數列表中意味著模板需要一個整數文字或一個.
- const int
- 別名引數這些引數可以接受本質上可以在編譯時確定的任何符號。這包括函式、其他模板、類等等。別名引數透過在引數名稱之前加上alias
- 來宣告。
- 元組引數
D 支援可變引數模板。這是一個高階主題,將在稍後介紹。
示例class LinkedStack(T)
{
private Node head;
class Node
{
T item;
Node next;
Node prev;
}
this()
{
head = new Node;
head.prev = head;
head.next = head;
}
void push(T t)
{
Node temp = new Node;
temp.item = t;
head.prev.next = temp;
temp.prev = head.prev;
temp.next = head;
head.prev = temp;
}
T pop()
{
Node temp = head.prev;
temp.prev.next = head;
head.prev = temp.prev;
return temp.item;
}
T peek()
{
return head.prev.item;
}
}
一個簡單的模板堆疊(使用連結串列實現)可能看起來像這樣
void main() {
auto list = new LinkedStack!(int);
list.push(1);
list.push(2);
list.push(3);
auto strList = new LinkedStack!(string);
strList.push("hello");
strList.push("world!");
}
要使用上面的示例,我們可以這樣說注意語法LinkedStack!(int)。這是模板例項化。它告訴編譯器生成LinkedStackT類的版本,其中int被替換為。上面示例中list注意語法變數的型別是。而strList的型別是。這兩種型別彼此不同。就繼承而言,它們僅在都繼承自Object.