指针、引用和常量
在了解了内置类型和对象类型以及 引用 和 指针的概念之后,比较一下所有可用的类型修改可能很有意义。
MQL5 中的引用仅用于描述函数和方法的参数。此外,对象类型参数必须通过引用传递。
void function(ClassOrStruct &object) { } // OK
|
这里的 ClassOrStruct 是类或结构体的名称。
只允许作为引用类型参数的自变量来传递变量(左侧值),不允许传递常量或表达式计算产生的临时值。
您无法创建引用类型的变量,也无法从函数返回引用。
ClassOrStruct &function(void) { return Class(); } // wrong
|
MQL5 中的指针仅适用于类对象。不支持指向内置类型变量或结构体变量的指针。
您可以将变量或函数参数声明为指向对象的指针,也可以从函数返回指向对象的指针。
ClassOrStruct *pointer; // OK
|
但您不能返回指向局部自动对象的指针,因为后者在函数退出时会被释放,并且指针将失效。
如果函数返回一个指向在函数内部使用 new 动态分配的对象的指针,则调用代码必须记住使用 delete 释放指针。
指针可以为 NULL,这一点与引用不同。指针参数可以具有默认值,但引用不能(“无法初始化引用”错误)。
void function(ClassOrStruct *object = NULL) { } // OK
|
链接和指针可以在参数说明中组合使用。因此,函数可以接受指向指针的引用:然后函数中对该指针的更改将在调用代码中生效。特别是,负责创建对象的工厂函数能以这种方式实现。
void createObject(ClassName *&ref)
|
确实,要从函数返回单个指针,通常习惯使用 return 语句,因此这个例子有些刻意。但是,在需要将指针数组传递到外部的情况下,在参数中引用数组是首选方案。例如,在一些用于处理带有 [键, 值] 对的映射类型容器类的标准库类(MQL5/Include/Generic/SortedMap.mqh、MQL5/Include/Generic/HashMap.mqh)中,有一些 CopyTo 方法可用于获取具有 CKeyValuePair 元素的数组。
int CopyTo(CKeyValuePair<TKey,TValue> *&dst_array[], const int dst_start = 0); |
dst_array 参数类型可能看起来不太熟悉:它是一个类模板。我们将在 下一章学习模板。目前,我们只关心这是一个对指针数组的引用。
const 修饰符对所有类型施加特殊行为。关于内置类型,我们已在 常量变量一节中进行了讨论。对象类型有其自身的特性。
如果变量或函数参数被声明为指针或对象的引用(引用仅在参数中有效),则其中的 const 修饰符会将可访问的方法和属性集限制为同样带有 const 修饰符的对象。换句话说,只有常量属性才能通过常量引用和指针访问。
当您尝试调用非常量方法或更改非常量字段时,编译器将生成错误:“对常量对象调用非常量方法”或“无法修改常量”。
非常量指针参数可以接受任何自变量(常量或非常量)。
需要注意的是,在指针说明中可以设置两个修饰符 const:一个引用对象,另一个引用指针:
- Class *pointer 指向对象的指针;对象和指针正常运行,不受任何限制;
- const Class *pointer 指向常量对象的指针;对于对象,只能使用常量方法和读取属性,但指针可以更改(为其赋予另一个对象的地址);
- const Class * const pointer 指向常量对象的常量指针;对于对象,只能使用常量方法和读取属性;指针不能更改;
- Class * const pointer 指向对象的常量指针;指针不能更改,但对象的属性可以更改。
以下面的 Counter 类 (CounterConstPtr.mq5) 为例。
class Counter
|
它人为地创建了公共变量 counter。该类还包含两个方法,其中一个是常量 (clone),另一个不是 (increment)。回想一下,常量方法无权更改对象的字段。
以下带有 Counter *ptr 类型参数的函数可以调用该类的所有方法并更改其字段。
void functionVolatile(Counter *ptr)
|
以下带有 const Counter *ptr 参数的函数将抛出几个错误。
void functionConst(const Counter *ptr)
|
最后,以下带有 const Counter * const ptr 参数的函数执行的操作更少。
void functionConstConst(const Counter * const ptr)
|
在 OnStart 函数中,我们声明了两个 Counter 对象(一个是常量,另一个不是),您可以调用这些函数,但有一些例外:
void OnStart()
|
首先,请注意,在非常量对象上尝试调用常量方法 increment 时,变量也会产生错误。
其次,constCounter 不能传递给 functionVolatile 函数,否则我们会收到错误“无法从常量指针转换为非常量指针”。
不过,这两个错误可以通过不带 const 修饰符的显式类型转换来避免。尽管我们不建议这样做。