Introduction to Classes in MQL5
What are Classes and Objects?
In MQL5, a class serves as a blueprint for creating objects. Think of a class as a template that defines the characteristics (data members or attributes) and behaviors (member functions or methods) that objects of that class will possess. An object is a specific instance of a class, a concrete entity created based on the class definition.
Benefits of Using Classes in MQL5
Object-oriented programming (OOP) with classes provides several advantages in MQL5 development:
- Modularity: Classes break down complex tasks into smaller, manageable units.
- Reusability: Classes can be reused across multiple projects.
- Maintainability: Changes within a class typically don’t affect other parts of the code.
- Abstraction: Hiding internal complexities and exposing only essential functionalities.
- Encapsulation: Bundling data and methods that operate on that data within a single unit, protecting data from accidental modification.
Basic Syntax for Class Declaration
The basic syntax for declaring a class in MQL5 is as follows:
class MyClass
{
// Class members (variables and functions)
private:
int myInteger;
public:
void MyMethod() {
// Method implementation
}
};
Creating Class Objects in MQL5
Declaring Class Objects
To create an object of a class, you first declare a variable of the class type, similar to declaring variables of built-in types (e.g., int, double).
MyClass myObject;
Instantiation of Class Objects using ‘new’
In MQL5, objects are typically created dynamically using the new operator. This allocates memory for the object on the heap. It is crucial to delete them later using delete operator. The advantage of allocating the objects on the heap is to avoid the stack overflow, especially in the long running programs such as Expert Advisors and Services.
MyClass *myObject = new MyClass();
Initializing Class Objects with Constructors
Constructors are special member functions that are automatically called when an object is created. They are used to initialize the object’s data members.
class MyClass
{
public:
int value;
// Default constructor
MyClass() : value(0) {}
// Parameterized constructor
MyClass(int initialValue) : value(initialValue) {}
};
// Usage:
MyClass *obj1 = new MyClass(); // Uses the default constructor
MyClass *obj2 = new MyClass(10); // Uses the parameterized constructor
Object Lifespan and Garbage Collection
Unlike some other programming languages, MQL5 doesn’t have automatic garbage collection. This means you are responsible for releasing the memory allocated to class objects when they are no longer needed using the delete operator. Failure to do so can lead to memory leaks, which can degrade performance over time, especially in long-running EAs. If you use new you must use delete.
MyClass *myObject = new MyClass();
// ... use the object
delete myObject;
myObject = NULL; // Optional, but good practice
Accessing Class Members
Public, Private and Protected Members
- Public: Accessible from anywhere.
- Private: Accessible only from within the class itself.
- Protected: Accessible from within the class and its derived classes (inheritance).
class MyClass
{
private:
int privateValue;
protected:
int protectedValue;
public:
int publicValue;
void SetPrivateValue(int value) {
privateValue = value; // Accessing private member from within the class
}
int GetPrivateValue() const { return privateValue; }
};
Using the Dot Operator to Access Members
To access public members of an object, you use the dot operator (.) when working with object variables, or the arrow operator (->) when working with object pointers.
MyClass *myObject = new MyClass();
myObject->publicValue = 10; // Accessing a public member using the arrow operator
int value = myObject->publicValue; // Accessing a public member using the arrow operator
//... delete myObject;
Accessing Members from Within the Class
Within a class’s member functions, you can directly access all members (public, private, and protected) using their names.
class MyClass
{
private:
int myValue;
public:
void SetValue(int value) {
myValue = value; // Accessing private member from within the class
}
int GetValue() const { return myValue; }
};
Advanced Class Object Usage
Arrays of Class Objects
You can create arrays of class objects, similar to arrays of built-in types.
MyClass *myObjects[10];
for(int i = 0; i < 10; i++) {
myObjects[i] = new MyClass(i); // Initialize each object
}
//... Use the objects
for(int i = 0; i < 10; i++) {
delete myObjects[i];
}
Pointers to Class Objects
Pointers to class objects are frequently used, especially when dealing with dynamic memory allocation and polymorphism.
MyClass *myObject = new MyClass();
MyClass *anotherPointer = myObject; // anotherPointer now points to the same object
//... use the object via pointers
delete myObject; // Only delete once!
Passing Class Objects as Function Parameters
Class objects can be passed as parameters to functions, either by value or by reference.
void MyFunction(MyClass *obj) // Pass by pointer
{
// Modify the object
obj->publicValue = 20;
}
MyClass *myObject = new MyClass();
MyFunction(myObject);
//...delete myObject;
Returning Class Objects from Functions
Functions can also return class objects, typically by pointer to avoid unnecessary copying. However, the object must outlive the function call, so be mindful of ownership and memory management.
MyClass* CreateMyClass() {
MyClass* obj = new MyClass();
return obj;
}
MyClass* myObject = CreateMyClass();
//... use the object
delete myObject;
Practical Examples and Use Cases
Creating a Simple Trade Management Class
class TradeManager
{
private:
double stopLossPips;
double takeProfitPips;
double lotSize;
public:
TradeManager(double sl, double tp, double lot) : stopLossPips(sl), takeProfitPips(tp), lotSize(lot) {}
bool OpenTrade(string symbol, int tradeType) {
// Implementation to open a trade with given parameters
double price = SymbolInfoDouble(symbol, tradeType == OP_BUY ? SYMBOL_ASK : SYMBOL_BID);
double stopLoss = tradeType == OP_BUY ? price - stopLossPips * Point() : price + stopLossPips * Point();
double takeProfit = tradeType == OP_BUY ? price + takeProfitPips * Point() : price - takeProfitPips * Point();
MqlTradeRequest request = {0};
MqlTradeResult result = {0};
request.action = TRADE_ACTION_DEAL;
request.symbol = symbol;
request.volume = lotSize;
request.type = tradeType;
request.price = price;
request.sl = stopLoss;
request.tp = takeProfit;
request.magic = 123456; // Magic number for identifying trades
request.type_filling = ORDER_FILLING_FOK;
request.type_time = ORDER_TIME_GTC;
bool success = OrderSend(request, result);
return success;
}
};
// Example usage in an Expert Advisor:
TradeManager *tradeMgr = new TradeManager(50, 100, 0.01);
if(tradeMgr->OpenTrade(Symbol(), OP_BUY)) {
Print("Trade opened successfully");
}
//... some code
delete tradeMgr;
Implementing a Custom Indicator Using Classes
Classes can encapsulate the logic for custom indicators, making the code more organized and reusable.
Developing an Expert Advisor with Class-Based Structure
Structuring an EA with classes promotes modularity and maintainability, especially for complex trading strategies. Separate classes can handle different aspects of the EA, such as trade management, risk management, and signal generation.