嵌套类型、命名空间和上下文运算符 '::'
类、结构体和联合体不仅可以在全局上下文中描述,还可以在其他类或结构体中描述。甚至还可以在函数内部进行定义。这样,您可以在适当的上下文中描述任何类或结构体运行所需的所有实体,从而避免潜在的名称冲突。
具体来说,在绘图程序中,用于存储坐标的结构体 Pair 到目前为止是在全局定义的。随着程序规模增长,很可能需要另一个名为 Pair 的实体(尤其考虑到这个名称相当通用)。因此,最好将结构体的描述移到 Shape (Shapes6.mq5) 类内部。
class Shape
|
嵌套说明的访问权限取决于指定的段修饰符。在本例中,我们将名称 Pair 设为公开可用。在 Shape 类内部,Pair 结构体类型的处理不会因转移而发生任何变化。但是,在外部代码中,您必须指定一个完全限定名称,其中包含外部类(上下文)的名称、上下文选择运算符 '::' 以及内部实体标识符本身。例如,要描述具有一对坐标的变量,可以这样写:
Shape::Pair coordinates(0, 0); |
描述实体时的嵌套层级不受限制,因此完全限定名称可以包含由 '::' 分隔的多层级标识符(上下文)。例如,我们可以将所有绘图类包装在外部类 Drawing 的 public 部分中。
class Drawing
|
然后,完全限定的类型名称(例如,用于 OnStart 或其他外部函数)将被加长:
Drawing::Shape::Rect coordinates(0, 0);
|
虽然这很不方便,但在包含许多类的大型项目中,有时这很有必要。在我们的小型项目中,这种方法仅用于演示技术可行性。
为了将逻辑相关的类和结构体组合成命名组,MQL5 提供了一种比将它们包含在“空”包装器类中更简单的方法。
命名空间使用关键字 namespace 声明,后跟命名空间的名称以及包含所有必要定义的代码块(用花括号括起)。以下是使用 namespace 的相同绘图程序的示例:
namespace Drawing
|
主要区别在于:空间的内部内容始终公开可用(访问修饰符在其中不适用),并且右花括号后没有分号。
我们为 Shape 类添加 move 方法,该方法以 Pair 结构体作为参数:
class Shape
|
然后,在 OnStart 函数中,您可以通过调用此函数来整理所有形状按给定值的偏移:
void OnStart()
|
请注意,Shape 和 Pair 类型必须分别使用全名 Drawing::Shape 和 Drawing::Shape::Pair 来描述。
可能有多个块的空间名称相同:它们的所有内容都将归入一个具有指定名称的逻辑统一上下文中。
全局上下文中定义的标识符,特别是 MQL5 API 的所有内置函数,也可以通过上下文选择运算符(前面不带任何符号)访问。例如,对 Print 函数的调用可能如下所示:
::Print("Done!"); |
从全局上下文中定义的任何函数进行调用时,不需要这样的条目。
如果类或结构体中定义了同名元素(函数、变量或常量),则其中需要这样的条目例如,我们将 Print 方法添加到 Shape 类中:
static void Print(string x)
|
由于派生类中 draw 方法的测试实现会调用 Print,因此它们现在被重定向到此 Print 方法:在多个相同标识符中,编译器会选择在更接近上下文中定义的标识符。在本例中,基类中的定义比全局上下文更接近 Shape 类。因此,Shape 类的日志输出将被抑制。
但是,从函数 OnStart 调用 Print 仍然有效(因为它位于 Shape 类的上下文之外)。
void OnStart()
|
要修复类中的调试打印问题,您需要在所有 Print 调用之前添加一个全局上下文选择运算符:
class Rectangle : public Shape
|