The labels public, private and protected are access keywords. They determine whether a variable or function is available for use outside of a class. Here are the descriptions of the access keywords:
• Public members of a class are available for use outside of the class. This is the method by which the
program interacts with an object. Public members are generally functions that perform important tasks. Public functions can access and modify the private and protected members of a class.
• Private members of a class are only available for use by functions inside the class. A private member
members. (We'll discuss inheritance shortly.) Private members are generally internal functions and variables that are accessed by public members of a class.
• Protected members of a class are essentially private members that will be inherited by a derived class.
Use the protected keyword unless you're certain that you won't be deriving any classes from the current class.
This concept of hiding class members from the rest of the program is an OOP concept referred to as encapsulation. By hiding class members, we ensure that they won't be used or modified unnecessarily. The CIndicator class is meant to be used as a parent class for other indicator classes, such as a moving average indicator class. We've created this class to implement features that every indicator will use. For example, every indicator requires a variable to store the indicator handle, so we've created an integer variable named handle. We'll also need at least one dynamic array to hold indicator values. The first (and sometimes only) buffer in an indicator is referred to as the main buffer, so we've declared a double array named main[]. Both of these variables are protected, which means they can't be accessed outside of our class, and can only be accessed by public members of the class.
For the public members of our class, we've created a function named Main() to update the indicator values and access the values in the main[] array, and a Release() function to release the indicator from memory. Let's take a closer look at the public functions of our class. We'll start with the Main() function. This function copies indicator data to the main[] array using the handle variable to identify the indicator, and returns the value for the specified bar. Here is the function declaration for the Main() function:
double CIndicator::Main(int pShift=0) {
CopyBuffer(handle,0,0,MAX_COUNT,main);
double value = NormalizeDouble(main[pShift],_Digits); return(value);
}
This function declaration is placed on the global scope, after the CIndicator class declaration. Note the CIndicator:: right before the function identifier. The double-colon operator (::) is the scope resolution operator. It identifies a function as belonging to a specific scope – in this case, the scope is the CIndicator class.
Notice also that our protected handle and main[] variables are used as parameters for the CopyBuffer() function. The only way we can access protected and private members of our class is through a public class function. If you attempt to access these members from elsewhere in your program, you'll get a compilation error.
The preferred way to declare class functions is the method used above. But you can also declare a function in- line, inside the class declaration itself. This is useful for very short functions that consists of a single line or two. Here's an example using our Release() function:
class CIndicator {
public:
void Release() { IndicatorRelease(handle); } // ...
};
The Release() function is declared on a single line inside our class declaration. It consists of a call to the IndicatorRelease() function. The contents of the function are contained within the brackets. It is not required to have a semicolon after the closing bracket, although the compiler will not complain if you do.
Constructors
When an object is created from a class, a function called the constructor is executed automatically. The constructor is used to initialize the variables inside our object. If no constructor is explicitly defined, then the compiler creates a default constructor to initialize the variables. This default constructor is not visible to the programmer.
In our CIndicator function, we have declared our own default constructor, also called CIndicator(). The name of a constructor must match that of the class identifier. It is not necessary to specify a return type for a default constructor, as the type is always void. The access level of a constructor must be public.
Here is the CIndicator() constructor declaration inside the CIndicator class declaration: class CIndicator { public: CIndicator(); // ... };
And here is our explicitly defined class constructor. The only purpose of our constructor is to set our main[] array as a series array. We'll talk more about series arrays later in the book:
CIndicator::CIndicator(void) {
ArraySetAsSeries(main,true); }
Remember, you do not need to explicitly declare a default constructor if you do not need one. If there are actions you want carried out automatically upon the creation of an object, then create a default constructor to do this.
There are more advanced things you can do with constructors, such as parametric constructors and
initialization lists. There is also the destructor, which is called upon destruction of an object. Since we won't be using those features in this book, it will be up to the reader to learn more. You can learn about constructors and destructors in the MQL5 Reference under Language Basics > Data Types > Structures and Classes.