- OOP fundamentals: Abstraction
- OOP fundamentals: Encapsulation
- OOP fundamentals: Inheritance
- OOP fundamentals: Polymorphism
- OOP fundamentals: Composition (design)
- Class definition
- Access rights
- Constructors: default, parametric, and copying
- Destructors
- Self-reference: this
- Inheritance
- Dynamic creation of objects: new and delete
- Pointers
- Virtual methods (virtual and override)
- Static members
- Nested types, namespaces, and the context operator '::'
- Splitting class declaration and definition
- Abstract classes and interfaces
- Operator overloading
- Object type сasting: dynamic_cast and pointer void *
- Pointers, references, and const
- Inheritance management: final and delete
Pointers, references, and const
After learning about built-in and object types, and the concepts of reference and pointer, it probably makes sense to do a comparison of all available type modifications.
References in MQL5 are used only when describing parameters of functions and methods. Moreover, object type parameters must be passed by reference.
void function(ClassOrStruct &object) { } // OK
|
Here ClassOrStruct is the name of the class or structure.
It is allowed to pass only variables (LValue) as an argument for a reference type parameter, but not constants or temporary values obtained as a result of expression evaluation.
You cannot create a variable of a reference type or return a reference from a function.
ClassOrStruct &function(void) { return Class(); } // wrong
|
Pointers in MQL5 are available only for class objects. Pointers to variables of built-in types or structures are not supported.
You can declare a variable or function parameter of type a pointer to an object, and also return a pointer to an object from the function.
ClassOrStruct *pointer; // OK
|
However, you cannot return a pointer to a local automatic object, because the latter will be freed when the function exits, and the pointer will become invalid.
If the function returned a pointer to an object dynamically allocated within the function with new, then the calling code must "remember" to free the pointer with delete.
A pointer, unlike a reference, can be NULL. Pointer parameters can have a default value, but references can't ("reference cannot be initialized" error).
void function(ClassOrStruct *object = NULL) { } // OK
|
Links and pointers can be combined in a parameter description. So a function can take a reference to a pointer: and then changes to the pointer in the function will become available in the calling code. In particular, the factory function, which is responsible for creating objects, can be implemented in this way.
void createObject(ClassName *&ref)
|
True, to return a single pointer from a function, it is usually customary to use the return statement, so this example is somewhat artificial. However, in those cases when it is necessary to pass an array of pointers outside, a reference to it in the parameter becomes the preferred option. For example, in some classes of the standard library for working with container classes of the map type with [key, value] pairs (MQL5/Include/Generic/SortedMap.mqh, MQL5/Include/Generic/HashMap.mqh) there are methods CopyTo for getting arrays with elements CKeyValuePair.
int CopyTo(CKeyValuePair<TKey,TValue> *&dst_array[], const int dst_start = 0); |
The parameter type dst_array may seem unfamiliar: it's a class template. We will learn about templates in the next chapter. Here, for now, the only important thing for us is that this is a reference to an array of pointers.
The const modifier imposes special behavior for all types. In relation to built-in types, it was discussed in the section on Constant variables. Object types have their own characteristics.
If a variable or function parameter is declared as a pointer or a reference to an object (a reference is only in the case of a parameter), then the presence of the modifier const on them limits the set of methods and properties that can be accessed to only those that also have the modifier const. In other words, only constant properties are accessible through constant references and pointers.
When you try to call a non-const method or change a non-const field, the compiler will generate an error: "call non-const method for constant object" or "constant cannot be modified".
A non-const pointer parameter can take any argument (constant or non-constant).
It should be borne in mind that two modifiers const can be set in the pointer description: one will refer to the object, and the second to the pointer:
- Class *pointer is a pointer to an object; the object and the pointer work without limitations;
- const Class *pointer is a pointer to a const object; for the object, only constant methods and reading properties are available, but the pointer can be changed (assigned to it the address of another object);
- const Class * const pointer is a const pointer to a const object; for the object, only const methods and reading properties are available; the pointer cannot be changed;
- Class * const pointer is a const pointer to an object; the pointer cannot be changed, but the properties of the object can be changed.
Consider the following class Counter (CounterConstPtr.mq5) as an example.
class Counter
|
It artificially made the public variable counter. The class also has two methods, one of which is constant (clone), and the second is not (increment). Recall that a constant method does not have the right to change the fields of an object.
The following function with the Counter *ptr type parameter can call all methods of the class and change its fields.
void functionVolatile(Counter *ptr)
|
The following function with the parameter const Counter *ptr will throw a couple of errors.
void functionConst(const Counter *ptr)
|
Finally, the following function with the parameter const Counter * const ptr does even less.
void functionConstConst(const Counter * const ptr)
|
In the function OnStart, where we have declared two Counter objects (one is constant and the other is not), you can call these functions with some exceptions:
void OnStart()
|
First, note that variables also generate an error when trying to call a const method increment on a non-const object.
Secondly, constCounter cannot be passed to the functionVolatile function we get the error "cannot convert from const pointer to nonconst pointer".
However, both errors can be circumvented by explicit type casting without the const modifier. Although this is not recommended.