目录
本机枚举与托管枚举
单个类解决方案
五个类解决方案
六个类解决方案
自 2006 年 6 月介绍安装“从 C++ 到 .NET”的主题后,我就着手撰写一系列专栏,深入探讨如何使用 Visual C++ 2005 中的 C++/CLI 语言扩展,将有效的本机 C++ 应用程序移到 Microsoft .NET Framework 上。我所移动的示例应用程序是文本查询语言 (Text Query Language, TQL),这是在 1996 年针对我编写的书籍 C++ Primer 的第三版而开发的。
六月份,我开始了一系列专栏,介绍如何包装本机 TQL 应用程序,接着在 2006 年 8 月份,我仔细检查并整理了代码。上个月,我概要介绍了如何在 .NET Framework 中使用正则表达式。您可以通过从 C++ 到 .NET(2006 年 6 月)和从 C++ 到 .NET(2006 年 6 月)找到这些以前的专栏文章。
TQL 设计用于支持两种操作:
对于每个唯一字词,将用户指定的随机文本文件规范化为行号匹配项的映射。
根据该映射处理用户查询,以显示文本的匹配行。
设计文本规范化的目的是为了阐释集合类的重要作用。在本机实现中,它使用标准模板库 (Standard Template Library, STL)。在后续的某个专栏中,我将着眼于这一设计内容,以深入探讨 .NET 基类库 (BCL) 的泛型集合命名空间。设计文本查询支持的目的是为了阐述一种重要但很基本的类设计。而 C++/CLI 编程设计的这一方面正是我要在本专栏中着重介绍的内容。
通过 TQL,用户可以使用一系列表示关系的逻辑标记来查询文本文件,这些逻辑标记包括:&& 表示“与”;|| 表示“或”;而 ! 表示“非”。因此,若要找到 Holmes 的所有匹配项,只需键入“Holmes”(不包括引号)。如果您希望查找其中不出现 Holmes 的所有文本行,只需在 Holmes 的前面附加“非”运算符 (!Holmes)。如果您的目标是查找 Sherlock 或 Holmes 的所有匹配项,则需要使用“或”运算符将这两个字词连接起来 (Sherlock || Holmes)。最后,要找到 Sherlock 且后面紧跟 Holmes 的所有匹配项,则需要使用“与”运算符连接这两个字词 (Sherlock && Holmes)。
不可否认,在今天的搜索引擎世界中,这似乎很微不足道。但是,对于考察各种类设计策略而言,这种方法确实是对症下药。请记住,本机实现的历史已经有十多年了 - 我在此只是使用 C++/CLI 将其改写,以用于 .NET Framework 中。
这一设计的候选抽象概念是用户可能使用的四种类型的查询。起初,我们将它们表示为一系列枚举,如下所示:
enum EQueryType { // native enum
我之所以选择这种方法,原因有二。第一,您将需要此枚举以实施我的一种设计解决方案;第二,我希望提供有关使用 C++/CLI 实现枚举支持的简要教程。
qWord = 1, qNot, qOr, qAnd
};
本机枚举与托管枚举
不熟悉 C++/CLI 的编程人员所询问的第一个问题是我刚才介绍的本机枚举与类似如下的等效托管枚举之间有何差异:
class enum EQueryType : Byte { // managed enum
qWord = 1, qNot, qOr, qAnd
};
主要差异有三个。
第一个差异是大小管理的粒度。在本例中,我指定一个字节的存储空间足以表示每个枚举器值(共四个值)。此功能也已被扩展到 C++ 的本机支持中,而不仅仅在 Visual C++ 中(非标准扩展)。
第二个差异是枚举器的范围。在托管环境中,枚举可以保持自己的范围,因而,它可以采用与类封装其成员可见性的相同方式来封装其枚举器的可见性。当然,这对于本机枚举并不正确,因为枚举器全部分散到封闭范围内。当组合来自多个站点的模块时,这可能会成为一个问题。为防止枚举器污染全局命名空间,本机 C++ 的一个设计惯例是在类中嵌套枚举,如下所示:
class ios {
public:
enum iostates { read, write, append };
// ...
}
要访问这些枚举器中的任何一个,可以在枚举器前面加上类范围运算符,例如 ios::read。这对于托管枚举并不是好的设计惯例,因为它会导致两层封装,如 ios::iostates::read。因此,托管枚举通常在类的共享范围内定义,其作用是支持本机枚举,而不是在该类中提供支持。
第三个差异是:与整数相比,托管枚举与对象更为相似。这就允许正确地解析重载函数,如下所示:
void f( int );
void f( Object^ );
EQueryType et;
f( et ); // which one?
如果托管枚举被视为整型 - 与本机枚举一样 - 则此调用必须匹配 f(int)。然而,如果公共类型系统 (CTS) 中的后备枚举类属于从对象中派生的值类型,则其类型必须明确绑定到对象,而不绑定到整数。因此,可以得出结论,托管枚举属于一种对象,而不是一种整数。
我希望这点已很清楚,因为我没有时间更有力地就这一结论展开讨论。尽管这是一个合理的结论,但存在令人遗憾的副作用:没有将托管枚举或它的其中一个枚举器隐式转换为整数的机制,因此,每次用作算术值时都需要显式转换,这的确比较麻烦。
因此,TQL 应用程序具有四种需要处理的具体类型:字词 (Word)、与 (And)、或 (Or) 和非 (Not)。但是,需要多少个类呢?我的设计解决方案提供了 1、5 或 6 个类。下面我们逐步探讨设计方法,并了解哪些内容对您有启发作用。