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.