Die Reihenfolge der Erstellung und Zerstörung von Objekten in MQL5

MetaQuotes | 11 Januar, 2016

Worum geht es in diesem Beitrag?

Das Konzept von MQL5-Programmen ist die objektorientierte Programmierung (OOP). Dies sorgt nicht nur für neue Möglichkeiten bei der Erstellung benutzerdefinierter Bibliotheken, sondern ermöglicht es Ihnen auch, vollständige und getestete Klassen von anderen Entwicklern zu benutzen. In der im MetaTrader 5 Client Terminal enthaltenen Standardbibliothek befinden sich hunderte Klassen mit tausenden Methoden.

Um alle Vorteile der OOP nutzen zu können, müssen wir einige Details zur Erstellung und Löschung von Objekten in MQL5-Programmen klären. Die Erstellung und Löschung von Objekten wird in der Dokumentation kurz beschrieben. Dieser Beitrag illustriert dieses Thema durch einige Beispiele.

Initialisieren und Deinitialisieren globaler Variablen

Die Initialisierung globaler Variablen findet direkt nach dem Start des MQL5-Programms und vor dem Aufruf von Funktionen statt. Während der Initialisierung werden Variablen einfacher Typen Ausgangswerte zugewiesen und der Konstruktor für Objekte aufgerufen, falls er darin deklariert ist. 

Deklarieren wir als Beispiel zwei Klassen, CObjectA und CObjectB. Jede Klasse hat einen Konstruktor und einen Destruktor mit einer einfachen Print()-Funktion. Deklarieren wir die Variablen dieser Klassentypen global und führen das Script durch.

//+------------------------------------------------------------------+
//|                                         GlobalVar_TestScript.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
class CObjectA
  {
public:
                     CObjectA(){Print(__FUNCTION__," Constructor");}
                    ~CObjectA(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectB
  {
public:
                     CObjectB(){Print(__FUNCTION__," Constructor");}
                    ~CObjectB(){Print(__FUNCTION__," Destructor");}
  };
//--- declaring the objects globally
CObjectA first;
CObjectB second;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   Print(__FUNCTION__);
  }

Das Ergebnis des Scripts wird im Logbuch des Experts angezeigt:

GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectA::ObjectA  Constructor
GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectB::ObjectB  Constructor
GlobalVar_TestScript (EURUSD,H1)    13:05:07    OnStart
GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectB::~ObjectB  Destructor
GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectA::~ObjectA  Destructor

Aus dem Logbuch ist ersichtlich, dass die Reihenfolge der Initialisierung der Reihenfolge der Deklaration der Variablen im Script GlobalVar_TestScript.mq5 entspricht und die Deinitialisierung vor der Beendigung des MQL5-Programms in umgekehrter Reihenfolge erfolgt.

Initialisieren und Deinitialisieren lokaler Variablen

Lokale Variablen werden am Ende des Programmblocks, in dem sie deklariert sind, und in umgekehrter Reihenfolge ihrer Deklaration deinitialisiert. Der Programmblock ist ein zusammengesetzter Operator, der Teil eines switch-Operators, von Schleifenoperatoren (for, while und do-while), des Körpers einer Funktion oder eines if-else-Operators sein kann.

Lokale Variablen werden nur initialisiert, wenn Sie im Programm verwendet werden. Wenn eine Variable deklariert ist, aber der Code-Block, in dem sie deklariert ist, nicht ausgeführt wird, dann wird diese Variable nicht erstellt und somit nicht initialisiert. 

Um dies zu illustrieren, gehen wir zu unseren Klassen CObjectA und CObjectB zurück und erstellen die neue Klasse CObjectC. Klassen werden weiterhin global deklariert, aber die Variablen dieser Klassen werden jetzt lokal in der OnStart()-Funktion deklariert.

Deklarieren wir die Variable der CObjectA-Klasse explizit in der ersten Zeile der Funktion. Objekte der Klassen CObjectB und CObjectC werden in separaten Blöcken deklariert, die abhängig vom Wert der Eingabevariable execute ausgeführt werden. Im MetaEditor werden Eingabevariablen von MQL5-Programmen braun gekennzeichnet.

//+------------------------------------------------------------------+
//|                                          LocalVar_TestScript.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property script_show_inputs
//--- input parameters
input bool     execute=false;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectA
  {
public:
                     CObjectA(){Print(__FUNCTION__," Constructor");}
                    ~CObjectA(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectB
  {
public:
                     CObjectB(){Print(__FUNCTION__," Constructor");}
                    ~CObjectB(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectC
  {
public:
                     CObjectC(){Print(__FUNCTION__," Constructor");}
                    ~CObjectC(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CObjectA objA;
//--- this block will NOT be executed if execute==false
   if(execute)
     {
      CObjectB objB;
     }
//--- this block WILL be executed if execute==false
   if(!execute)
     {
      CObjectC objC;
     }
  }
//+------------------------------------------------------------------+

Das Ergebnis ist:

LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectA::CObjectA  Constructor
LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectC::CObjectC  Constructor
LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectC::~CObjectC  Destructor
LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectA::~CObjectA  Destructor

Das Objekt der CObjectA-Klasse wird immer automatisch zuerst initialisiert, unabhängig vom Wert des Eingabeparameters execute. Danach wird entweder Objekt objB oder Objekt objC automatisch initialisiert. Welches Objekt initialisiert wird, hängt davon ab, welcher Block gemäß dem Wert des Eingabeparameters execute ausgeführt wird. Standardmäßig hat dieser Parameter den Wert false. In diesem Fall erfolgt nach der Initialisierung der objA-Variable die Initialisierung der objC-Variable. Dies ist in der Konstruktor- und Destruktor-Ausführung ersichtlich.

Doch ganz egal, wie die Reihenfolge der Initialisierung aussieht (unabhängig vom Parameter execute), findet die Deinitialisierung von komplexen Variablen in umgekehrter Reihenfolge ihrer Initialisierung statt. Das gilt sowohl für lokale als auch für globale Objekte, die automatisch erstellt wurden. Sie unterscheiden sich in diesem Fall nicht voneinander.

Initialisieren und Deinitialisieren dynamisch erstellter Objekte

Zusammengesetzte Objekte werden in MQL5 automatisch initialisiert. Wenn Sie den Prozess der Objekterstellung manuell kontrollieren möchten, müssen Sie allerdings Objekt-Pointer verwenden. Eine Variable, die als Objekt-Pointer einer beliebigen Klasse deklariert wurde, beinhaltet nicht das Objekt selbst und es findet keine automatische Initialisierung dieses Objekts statt.

Pointer können lokal und/oder global deklariert werden und können gleichzeitig mit dem leeren Wert NULL eines vererbten Typen initialisiert werden. Die Objekterstellung findet nur statt, wenn unabhängig von der Deklaration des Objekt-Pointers der Operator new auf den Objekt-Pointer angewendet wird.

Dynamisch erstellte Objekte werden mit dem Operator delete gelöscht, also müssen wir uns darum kümmern. Deklarieren wir als Beispiel zwei Variablen global: eine des Typen CObjectA und eine des Typen CObjectB und eine weitere Variable des Typen CObjectC mit Objekt-Pointer.

//+------------------------------------------------------------------+
//|                                       GlobalVar_TestScript_2.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
class CObjectA
  {
public:
                     CObjectA(){Print(__FUNCTION__," Constructor");}
                    ~CObjectA(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectB
  {
public:
                     CObjectB(){Print(__FUNCTION__," Constructor");}
                    ~CObjectB(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectC
  {
public:
                     CObjectC(){Print(__FUNCTION__," Constructor");}
                    ~CObjectC(){Print(__FUNCTION__," Destructor");}
  };
CObjectC *pObjectC;
CObjectA first;
CObjectB second;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   pObjectC=new CObjectC;
   Print(__FUNCTION__);
   delete(pObjectC);
  }
//+------------------------------------------------------------------+

Obwohl dieser dynamisch erstellte Objekt-Pointer pObjectC vor den statischen Variablen first und second deklariert wird, wird dieses Objekt nur dann initialisiert, wenn es durch den Operator new erstellt wird. In diesem Beispiel befindet sich der Operator new in der OnStart()-Funktion.

GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectA::CObjectA  Constructor
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectB::CObjectB  Constructor
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectC::CObjectC  Constructor
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    OnStart
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectC::~CObjectC  Destructor
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectB::~CObjectB  Destructor
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectA::~CObjectA  Destructor

Wenn die Ausführung des Programms in der OnStart()-Funktion den Operator erreicht,

   pObjectC=new CObjectC;

wird das Objekt initialisiert und der Konstruktor dieses Objekts aufgerufen. Anschließend führ das Programm den String

   Print(__FUNCTION__);

aus, der den folgenden Text ins Logbuch schreibt:

GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    OnStart

Anschließend wird das dynamisch erstellte Objekt durch Aufruf des Operators delete gelöscht:

   delete(pObjectC);

So werden Objekte während ihrer Erstellung durch den Operator new dynamisch initialisiert und durch den Operator delete gelöscht. 

Zwingende Voraussetzung: Alle Objekte, die durch den Ausdruck object_pointer=new Class_Name erstellt werden, müssen immer durch den Operator delete(object_pointer) gelöscht werden. Wenn ein dynamisch erstelltes Objekt (nach dem Ende des Blocks, in dem es initialisiert wurde) aus irgendeinem Grund nicht durch den Operator delete gelöscht wurde, wird eine entsprechende Meldung im Logbuch des Experts angezeigt.


Löschen von dynamisch erstellten Objekten

Wie bereits erwähnt, wird jedes dynamisch erstellte Objekt mithilfe des Operators new initialisiert und muss immer mithilfe des Operators delete gelöscht werden. Vergessen Sie jedoch nicht, dass der Operator new ein Objekt erstellt und einen Pointer zu diesem Objekt ausgibt.  Das erstellte Objekt selbst befindet sich nicht in der Variable, die den Objekt-Pointer enthält. Sie können mehrere Pointer deklarieren und sie demselben Objekt-Pointer zuweisen.

//+------------------------------------------------------------------+
//|                                        LocalVar_TestScript_1.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|  simple class                                                    |
//+------------------------------------------------------------------+
class CItem
  {
public:
                     CItem(){Print(__FUNCTION__," Constructor");}
                    ~CItem(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- declaring the first object pointer array 
   CItem* array1[5];
//--- declaring the first object pointer array 
   CItem* array2[5];
//--- filling arrays in the loop
   for(int i=0;i<5;i++)
     {
      //--- creating a pointer for the first array using new operator
      array1[i]=new CItem;
      //--- creating a pointer for the second array via copy from the first array
      array2[i]=array1[i];
     }
   // We "forgot" to delete objects before exiting the function. See "Experts" tab.
  }
//+------------------------------------------------------------------+

Sie erhalten eine Benachrichtigung darüber, dass mehrere ungelöschte Objekte übrig sind. Doch es sind nur 5 ungelöschte Objekte anstatt von 10, weil der Operator new nur 5 Objekte erstellt hat.

(GBPUSD,H1)    12:14:04    CItem::CItem  Constructor
(GBPUSD,H1)    12:14:04    CItem::CItem  Constructor
(GBPUSD,H1)    12:14:04    CItem::CItem  Constructor
(GBPUSD,H1)    12:14:04    CItem::CItem  Constructor
(GBPUSD,H1)    12:14:04    CItem::CItem  Constructor
(GBPUSD,H1)    12:14:04    5 undeleted objects left

Auch wenn der Destruktor für dynamisch erstellte Objekte nicht aufgerufen wird (das Objekt nicht mithilfe des Operators delete gelöscht wird), wird der Speicher dennoch gelöscht. Allerdings wird im Logbuch "Experts" angegeben, dass dieses Objekt nicht gelöscht wurde. Dies hilft Ihnen beim Auffinden einer falschen Objektverwaltung und beim Beheben des Fehlers.

Im nächsten Beispiel versuchen wir, die Pointer in zwei Pointer-Arrays zu löschen array1 und array2.

//+------------------------------------------------------------------+
//|                                        LocalVar_TestScript_2.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|  simple class                                                    |
//+------------------------------------------------------------------+
class CItem
  {
public:
                     CItem(){Print(__FUNCTION__," Constructor");}
                    ~CItem(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- declaring the first object pointer array
   CItem* array1[5];
//--- declaring the second object pointer array
   CItem* array2[5];
//--- filling arrays in the loop
   for(int i=0;i<5;i++)
     {
      //--- creating a pointer for the first array using new operator
      array1[i]=new CItem;
      //--- creating a pointer for the second array via copy from the first array
      array2[i]=array1[i];
     }
//--- deleting object using pointers of second array
   for(int i=0;i<5;i++) delete(array2[i]);
//--- let's try to delete objects using pointers of first array
   for(int i=0;i<5;i++) delete(array2[i]);
// in Experts tab there are messages about trying to delete invalid pointer
  }
//+------------------------------------------------------------------+

Das Ergebnis in der Registerkarte Experts sieht nun anders aus.

(GBPUSD,H1)    15:02:48    CItem::CItem  Constructor
(GBPUSD,H1)    15:02:48    CItem::CItem  Constructor
(GBPUSD,H1)    15:02:48    CItem::CItem  Constructor
(GBPUSD,H1)    15:02:48    CItem::CItem  Constructor
(GBPUSD,H1)    15:02:48    CItem::CItem  Constructor
(GBPUSD,H1)    15:02:48    CItem::~CItem  Destructor
(GBPUSD,H1)    15:02:48    CItem::~CItem  Destructor
(GBPUSD,H1)    15:02:48    CItem::~CItem  Destructor
(GBPUSD,H1)    15:02:48    CItem::~CItem  Destructor
(GBPUSD,H1)    15:02:48    CItem::~CItem  Destructor
(GBPUSD,H1)    15:02:48    delete invalid pointer
(GBPUSD,H1)    15:02:48    delete invalid pointer
(GBPUSD,H1)    15:02:48    delete invalid pointer
(GBPUSD,H1)    15:02:48    delete invalid pointer
(GBPUSD,H1)    15:02:48    delete invalid pointer

Die erstellten CItem-Objekte wurden in der ersten for()-Schleife erfolgreich gelöscht, doch weitere Versuche in der zweiten Schleife, Objekte zu löschen, die nicht existieren, haben zu Meldungen über ungültige Pointer geführt. Ein dynamisch erstelltes Objekt muss einmal gelöscht werden. Vor der Anwendung eines Objekt-Pointers muss es außerdem mithilfe der CheckPointer()-Funktion überprüft werden.

Prüfen von Pointern mithilfe der CheckPointer()-Funktion

CheckPointer() wird genutzt, um Pointer zu prüfen, und ermöglicht die Identifizierung des Pointer-Typen. Wenn Sie mit dynamisch erstellten Objekten arbeiten, gibt es zwei Möglichkeiten: 

  • Wiederherstellung am Ende des Ausführungsblocks
  • Versuch, bereits gelöschte Objekte zu löschen 

Sehen wir uns ein weiteres Beispiel an, das den Zusammenhang zwischen Objekten illustriert. Erstellen wir zwei Klassen: Die erste Klasse CItemArray enthält das Pointer-Array der zweiten Klasse CItem.

//+------------------------------------------------------------------+
//|                                        LocalVar_TestScript_3.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|  simple class                                                    |
//+------------------------------------------------------------------+
class CItem
  {
public:
                     CItem(){Print(__FUNCTION__," Constructor");}
                    ~CItem(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| class, containing pointer array of CItem class                   |
//+------------------------------------------------------------------+
class CItemArray
  {
private:
   CItem            *m_array[];
public:
                     CItemArray(){Print(__FUNCTION__," Constructor");}
                    ~CItemArray(){Print(__FUNCTION__," Destructor");Destroy();}
   void               SetArray(CItem &array[]);
protected:
   void               Destroy();
  };
//+------------------------------------------------------------------+
//|  filling pointers array                                          |
//+------------------------------------------------------------------+
CItemArray::SetArray(CItem &array[])
  {
   int size=ArraySize(array);
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++)m_array[i]=GetPointer(array[i]);
  }
//+------------------------------------------------------------------+
//|  releasing                                                       |
//+------------------------------------------------------------------+
CItemArray::Destroy(void)
  {
   for(int i=0;i<ArraySize(m_array);i++)
     {
      if(CheckPointer(m_array[i])!=POINTER_INVALID)
        {
         if(CheckPointer(m_array[i])==POINTER_DYNAMIC) delete(m_array[i]);
        }
      else Print("Invalid pointer to delete");
     }
  }

Die Klassen selbst sind frei von Fehlern, doch ihre Nutzung kann zu Überraschungen führen. Die erste Variante des Scripts:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CItemArray items_array;
   CItem array[5];
   items_array.SetArray(array);
  }

Die Ausführung dieser Script-Variante führt zu den folgenden Meldungen:

(GBPUSD,H1)    16:06:17    CItemArray::CItemArray  Constructor
(GBPUSD,H1)    16:06:17    CItem::CItem  Constructor
(GBPUSD,H1)    16:06:17    CItem::CItem  Constructor
(GBPUSD,H1)    16:06:17    CItem::CItem  Constructor
(GBPUSD,H1)    16:06:17    CItem::CItem  Constructor
(GBPUSD,H1)    16:06:17    CItem::CItem  Constructor
(GBPUSD,H1)    16:06:17    CItem::~CItem  Destructor
(GBPUSD,H1)    16:06:17    CItem::~CItem  Destructor
(GBPUSD,H1)    16:06:17    CItem::~CItem  Destructor
(GBPUSD,H1)    16:06:17    CItem::~CItem  Destructor
(GBPUSD,H1)    16:06:17    CItem::~CItem  Destructor
(GBPUSD,H1)    16:06:17    CItemArray::~CItemArray  Destructor
(GBPUSD,H1)    16:06:17    Invalid pointer to delete
(GBPUSD,H1)    16:06:17    Invalid pointer to delete
(GBPUSD,H1)    16:06:17    Invalid pointer to delete
(GBPUSD,H1)    16:06:17    Invalid pointer to delete

Da die Deklarierung der Klassenvariable CItemArray zuerst erfolgt, wird diese zuerst initialisiert und die Klasse destructor aufgerufen. Anschließend wird array[5] deklariert, das die Objekt-Pointer der CItem-Klasse enthält. Deshalb sehen wir fünf Meldungen über die Initialisierung jedes Objekts.

In der letzten Zeile dieses einfachen Scripts werden die Pointer aus dem Array array[5] in ein internes Objekt-Pointer-Array mit dem Namen items_array kopiert (siehe 'LocalVar_TestScript_4.mq5').

   items_array.SetArray(array);

Die Ausführung des Scripts wird angehalten und automatisch erstellte Objekte werden automatisch gelöscht. Das erste Objekt, das gelöscht wird, ist dasjenige, das als letztes initialisiert wurde – das Pointer-Array array[5]. Dies wird durch fünf Logbucheinträge über Aufrufe der CItem-Klasse destructor bestätigt. Dann folgt die Meldung über den Aufruf des Destruktors für das Objekt items_array, da dieses genau vor der Variable array[5] initialisiert wurde. 

Allerdings ruft der Destruktor der CArrayItem-Klasse die geschützte Destroy()-Funktion auf, die versucht, CItem-Objekte durch Pointer in m_array[] über den Operator delete zu löschen. Der Pointer wird als Erstes überprüft und falls er ungültig ist, werden die Objekte nicht gelöscht und die Meldung "Invalid pointer to delete" (Ungültiger zu löschender Pointer) angezeigt. 

Es gibt 5 solche Aufzeichnungen im Logbuch, d. h. alle Pointer im Array m_array[] sind ungültig. Das ist geschehen, weil die Objekte dieser Pointer bereits während der Deinitialisierung des Arrays array[] deinitialisiert wurden.

Optimieren wir unser Script, indem wir die Deklarationen der Variablen items_array und items_array[] austauschen.

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CItem array[5];
   CItemArray items_array;
   items_array.SetArray(array);
  }

Das korrigierte Script führt zu keinen Fehlern. Die Variable items_array wurde zuerst deinitialisiert, da sie als letzte deklariert wurde. Während ihrer Deinitialisierung wurde der Destruktor der ~CItemArray()-Klasse aufgerufen, der wiederum die Destroy()-Funktion aufgerufen hat. 

In dieser Deklarationsreihenfolge wird das Array items_array vor array[5] gelöscht. In der Destroy()-Funktion, die über den items_array-Destruktor aufgerufen wird, bestehen noch Pointer-Objekte, sodass keine Fehler entstehen.

Die korrekte Löschung dynamisch erstellter Objekte finden Sie auch im Beispiel der GetPointer()-Funktion. In diesem Beispiel wird die Destroy()-Funktion explizit aufgerufen, um die richtige Reihenfolge der Löschung von Objekten zu gewährleisten.

Fazit

Wie Sie sehen, ist die Erstellung und Löschung von Objekten einfach. Sehen Sie sich einfach alle Beispiele aus diesem Beitrag an. Dann können Sie Ihre eigenen Varianten von wechselseitigen Beziehungen zwischen automatisch und dynamisch erstellten Objekten erschaffen. 

Sie sollten Ihre Klassen immer auf die korrekte Löschung von Objekten prüfen und Ihre Destruktoren richtig auslegen, sodass es beim Zugriff auf ungültige Pointer nicht zu Fehlern kommt. Denken Sie daran, dass Sie bei der Verwendung von Objekten, die mithilfe des Operators new dynamisch erstellt werden, diese Objekte mithilfe des Operators delete korrekt löschen müssen.

In diesem Beitrag haben Sie nur die Reihenfolge der Erstellung und Löschung von Objekten in MQL5 gelernt. Die Organisation einer sicheren Arbeitsweise mit Objekt-Pointern liegt außerhalb des Umfangs dieses Beitrags.