MQL5 Classes, Structs & Inheritance: OOP Architecture Guide
Master MQL5 object-oriented programming with classes, structs, and inheritance. Learn memory management, access specifiers, virtual methods, and modular EA design.
What this MQL5 guide covers
MQL5 supports object-oriented programming based on C++, including class encapsulation, inheritance, and polymorphism.
Structs in MQL5 default to public member access and serve as lightweight value types for signals, ticks, and configuration parameters.
Classes default to private access, support single inheritance (class Derived : public Base), virtual methods, and heap allocation via new and delete.
Structs in MQL5 cannot inherit or declare virtual functions; polymorphic strategy components require classes.
Struct vs Class in MQL5: Core Differences
Both structs and classes in MQL5 can contain data members, constructor and destructor methods, and member functions. The key structural differences govern default access rights, inheritance capabilities, and allocation patterns.
A struct defaults to public access for its members and methods. A class defaults to private access, enforcing data encapsulation unless public or protected specifiers are declared.
| Feature | MQL5 Struct | MQL5 Class |
|---|---|---|
| Default Access | public | private |
| Inheritance | Not supported | Single inheritance supported |
| Virtual Methods | Not supported | Supported (polymorphism) |
| Memory Model | Value type (stack copy) | Reference or pointer type |
| Typical Use Case | Data records (MqlTick, signals) | Trading engines, indicator wrappers |
Inheritance and Virtual Methods in MQL5
MQL5 implements single class inheritance using the colon syntax: class CDerived : public CBase. Multiple inheritance is not supported in MQL5.
Declaring methods with the virtual keyword enables runtime polymorphism. A base pointer can store derived class instances and invoke the correct overridden implementation dynamically.
- virtual void OnTick() = 0; defines a pure virtual method, making the class abstract.
- Derived classes override methods without altering the calling code interface.
- Constructors execute from base to derived; destructors execute in reverse order.
Dynamic Allocation and Pointer Safety: CheckPointer
Objects in MQL5 can be declared statically on the stack or dynamically allocated on the heap using the new operator. Dynamic objects return a pointer (CStrategyBase*).
Every dynamic object created with new must be explicitly freed with delete in OnDeinit(). To prevent null pointer exceptions, always verify pointers with CheckPointer(ptr) != POINTER_INVALID before calling methods.
| CheckPointer Return | Meaning | Recommended Action |
|---|---|---|
| POINTER_INVALID | Pointer does not reference valid memory | Do not dereference; reinitialize or abort |
| POINTER_DYNAMIC | Allocated with new on the heap | Valid to call methods; must delete in OnDeinit |
| POINTER_AUTOMATIC | Allocated on the stack (local variable) | Valid to call methods; cleans up automatically |
OOP Patterns in the MT5 Standard Library
The MetaTrader 5 Standard Library relies heavily on OOP architecture. Classes such as CTrade, CPositionInfo, COrderInfo, and CAccountInfo encapsulate low-level trade structures into clean methods.
Extending or composing standard library classes inside your custom strategy classes produces modular Expert Advisors that are easy to test, maintain, and adapt to different market regimes.
Implementation details & mechanics
Core implementation pattern
In OnInit(), dynamically instantiate a strategy class: g_strategy = new CRsiStrategy(_Symbol, _Period, InpPeriod, InpThreshold). Verify pointer validity with CheckPointer(g_strategy) == POINTER_DYNAMIC. In OnTick(), delegate evaluation to g_strategy.Update() and g_strategy.CheckSignal(). If the returned TradeSignal struct signals a buy, execute via a shared CTrade instance.
Error handling & safety rules
In OnDeinit(const int reason), safely deallocate the strategy instance: if(CheckPointer(g_strategy) == POINTER_DYNAMIC) delete g_strategy; g_strategy = NULL. Position close checks call g_strategy.CheckExit() to keep exit condition evaluation decoupled from EA event handlers.
MQL5 Code Implementation Code
//+------------------------------------------------------------------+
//| ClassesStructsInheritanceEA.mq5 |
//| Copyright 2026, Pineify Guides Series |
//| https://pineify.app |
//+------------------------------------------------------------------+
#property copyright "Pineify Guides Series"
#property link "https://pineify.app/mql5/classes-structs-inheritance-guide"
#property version "1.00"
#property strict
#include <Trade\Trade.mqh>
//--- 1. Plain Data Structure (Struct)
struct TradeSignal
{
bool isValid;
int direction; // 1 = Buy, -1 = Sell, 0 = Flat
double price;
datetime timestamp;
string comment;
};
//--- 2. Abstract Base Class
class CStrategyBase
{
protected:
string m_symbol;
ENUM_TIMEFRAMES m_timeframe;
public:
CStrategyBase(const string symbol, const ENUM_TIMEFRAMES tf)
: m_symbol(symbol), m_timeframe(tf) {}
virtual ~CStrategyBase() {}
// Pure virtual methods enforcing contract in derived classes
virtual bool Init() = 0;
virtual void Update() = 0;
virtual TradeSignal CheckSignal() = 0;
};
//--- 3. Derived Concrete Strategy Class
class CRsiStrategy : public CStrategyBase
{
private:
int m_period;
double m_oversold;
int m_handle;
double m_buffer[];
public:
CRsiStrategy(const string symbol, const ENUM_TIMEFRAMES tf,
const int period, const double oversold)
: CStrategyBase(symbol, tf),
m_period(period),
m_oversold(oversold),
m_handle(INVALID_HANDLE) {}
virtual ~CRsiStrategy()
{
if(m_handle != INVALID_HANDLE)
IndicatorRelease(m_handle);
}
virtual bool Init() override
{
m_handle = iRSI(m_symbol, m_timeframe, m_period, PRICE_CLOSE);
if(m_handle == INVALID_HANDLE)
return false;
ArraySetAsSeries(m_buffer, true);
return true;
}
virtual void Update() override
{
if(m_handle != INVALID_HANDLE)
CopyBuffer(m_handle, 0, 0, 2, m_buffer);
}
virtual TradeSignal CheckSignal() override
{
TradeSignal signal;
ZeroMemory(signal);
if(ArraySize(m_buffer) < 2)
return signal;
// Detect oversold rebound: previous bar below oversold, latest crossed above
if(m_buffer[1] < m_oversold && m_buffer[0] >= m_oversold)
{
signal.isValid = true;
signal.direction = 1; // Long
signal.price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
signal.timestamp = TimeCurrent();
signal.comment = "RSI Oversold Rebound";
}
return signal;
}
};
//--- Global Variables
CStrategyBase *g_strategy = NULL;
CTrade g_trade;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Instantiate derived class through base pointer
g_strategy = new CRsiStrategy(_Symbol, _Period, 14, 30.0);
if(CheckPointer(g_strategy) == POINTER_INVALID)
{
Print("Failed to allocate strategy instance.");
return(INIT_FAILED);
}
if(!g_strategy.Init())
{
Print("Strategy initialization failed.");
return(INIT_FAILED);
}
Print("OOP EA initialized successfully.");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Safe dynamic memory cleanup
if(CheckPointer(g_strategy) == POINTER_DYNAMIC)
{
delete g_strategy;
g_strategy = NULL;
}
PrintFormat("Strategy instance deleted. Deinit reason: %d", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(CheckPointer(g_strategy) == POINTER_INVALID)
return;
g_strategy.Update();
TradeSignal sig = g_strategy.CheckSignal();
if(sig.isValid && sig.direction == 1)
{
PrintFormat("Signal triggered: %s at price %.5f", sig.comment, sig.price);
// Execute order via CTrade
}
}
Copy this code into MetaEditor, save it in your MQL5 folder (Experts, Scripts, or Include), and compile with F7.
Generate custom MQL5 code with Pineify AI
Describe the trade execution, indicator calculations, risk management, or data processing logic you need. Pineify produces clean, typed MQL5 source code ready to compile in MetaEditor.
Frequently Asked Questions
Related MQL5 pages
Technical references
Risk and testing note
Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.