- Ganzzahlige Typen
- Realtypen (double, float)
- Komplexen Zahlen (complex)
- Typ string
- Strukturen, Klassen und Schnittstellen
- Objekt des dynamischen Arrays
- Matrizen und Vektoren
- Typenreduzierung
- Typ void und Konstante NULL
- Benutzerdefinierte Typen
- Objektanzeiger
- Referenzen. Modifikator& und Schluesselwort this
Strukturen, Klassen und Schnittstellen
Strukturen
Eine Struktur umfasst eine Reihe von Elementen beliebigen Typs (außer dem Typ void). Auf diese Weise gruppiert eine Struktur logisch verbundene Daten unterschiedlicher Typen.
Strukturdeklaration
Der Strukturdatentyp wird wie folgt bestimmt:
struct Strukturname
|
Der Strukturname kann nicht als Identifikator verwendet werden (Name der Variable oder Funktion). Man bedenke, dass die Strukturelemente in MQL5 ohne Ausrichtung direkt aufeinander folgen. In der Sprache C++ wird diese Festlegung an den Kompiler mit der Anweisung gerichtet.
#pragma pack(1) |
Wenn es notwendig ist, eine andere Ausrichtung in der Struktur durchzuführen, muss man "Platzhalter" der gewünschten Größen verwenden.
Beispiel:
struct trade_settings
|
Eine solche Beschreibung ausgerichteter Strukturen ist nur für die Übergabe an importierte dll-Funktionen notwendig.
Achtung: dieses Beispiel zeigt falsch entworfene Daten. Es ist besser, zuerst die Daten von Take-Profit und Stop-Loss mit dem Typ double und dann das Slippage-Member des Uchartyps zu deklarieren. In diesem Fall ist die interne Darstellung der Daten immer gleich, unabhängig von dem im #pragma pack() angegebenen Wert.
Wenn die Struktur Variablen der Art string und/oder Objekt des dynamischen Feldes enthält, weist der Kompiler für eine solche Struktur einen impliziten Konstruktor zu, in dem alle Mitglieder vom Typ string zurückgesetzt werden und die dynamischen Array-Objekt werden korrekt initialisiert.
Einfache Strukturen
Strukturen, die keine Zeichenketten, Klassenobjekte, Pointer und Objekte von dynamischen Arrays enthalten, werden als einfache Strukturen bezeichnet. Variablen einfacher Strukturen sowie deren Arrays können als Parameter an importierte Funktionen aus DLL. übergeben werden.
Das Kopieren von einfachen Strukturen ist nur in zwei Fällen erlaubt:
- Wenn die Objekte zum gleichen Strukturtyp gehören.
- Wenn die Objekte linear miteinander verbunden sind, was bedeutet, dass eine Struktur ein Nachkomme einer anderen ist.
Um ein Beispiel zu geben, entwickeln wir die nutzerdefinierte Struktur CustomMqlTick mit ihrem Inhalt, der identisch ist mit der integrierten Struktur MqlTick. Der Kompiler erlaubt es nicht, den Wert des MqlTick-Objekts in das Objekt vom Typ CustomMqlTick zu kopieren. Auch die direkte Typenumwandlung zum benötigten Typ verursacht einen Kompilerfehler:
//--- Kopieren einfacher Strukturen verschiedener Typen ist verboten
|
Daher bleibt nur eine Option - das Kopieren der Werte der Strukturelemente, eines nach dem anderen. Es ist weiterhin erlaubt, die Werte des gleichen Typs von CustomMqlTick zu kopieren.
CustomMqlTick my_tick1,my_tick2;
|
Die Funktion ArrayPrint() wird aufgerufen, um die Werte des Arrays arr[] im Journal auszudrucken.
//+------------------------------------------------------------------+
|
Das zweite Beispiel zeigt die Eigenschaften des linearen Kopierens einfacher Strukturen. Angenommen, wir haben die Grundstruktur Tier, von der die Strukturen für Katze und Hund abgeleitet werden. Wir können die Objekte Tier und Katze sowie Tier und Hund ineinander kopieren, aber wir können Katze und Hund nicht ineinander kopieren, obwohl beide Nachkommen der Struktur Tier sind.
//--- Struktur zur Beschreibung von Hund
|
Der ganze Code des Beispiels:
//--- Basisstruktur zur Beschreibung von Tier
|
Ein anderer Weg einfache Strukturen zu kopieren ist die Verwendung von unions. Die Objekte der Struktur sollten Mitglieder der gleichen union sein - siehe das Beispiel für union.
Zugang zu Strukturgliedern
Strukturname ist ein neuer Datentyp und ermöglicht Variablen dieses Typs zu vereinbaren. Struktur kann nur einmal im Rahmen dieses Projekts vereinbart werden. Zugriff auf die Strukturmitglieder erhält man durch die Operation Punkt (.) durchgeführt.
Beispiel:
struct trade_settings
|
'pack' zum Ausrichten von Struktur- und Klassenfeldern #
Das spezielle Attribut pack ermöglicht die Ausrichtung von Struktur- oder Klassenfeldern.
pack([n]) |
wobei n einer der folgenden Werte ist: 1, 2, 4, 8 oder 16. Es kann auch fehlen.
Beispiel:
struct pack(sizeof(long)) MyStruct
|
'pack(1)' wird standardmäßig auf Strukturen angewendet. Das bedeutet, dass sich die Strukturelemente nacheinander im Speicher befinden und die Strukturgröße gleich der Summe der Größe ihrer Elemente ist.
Beispiel:
//+------------------------------------------------------------------+
|
Das Ausrichten der Strukturfelder kann erforderlich sein, wenn Daten mit Bibliotheken von Drittanbietern (*.DLL) ausgetauscht werden, bei denen diese Ausrichtung angewendet wird.
Lassen Sie uns anhand einiger Beispiele zeigen, wie die Ausrichtung funktioniert. Wir werden eine Struktur verwenden, die aus vier Mitgliedern besteht und nicht ausgerichtet ist.
//--- Einfache Struktur ohne Ausrichtung
|
Strukturfelder sind gemäß der Deklarationsreihenfolge und Typengröße nacheinander im Speicher angeordnet. Die Strukturgröße ist 15, während ein Offset zu den Strukturfeldern in den Arrays undefiniert ist.
Deklarieren wir nun die gleiche Struktur mit der Ausrichtung von 4 Bytes und führen den Code aus.
//+------------------------------------------------------------------+
|
Die Strukturgröße hat sich so verändert, dass alle Mitglieder von 4 Bytes und mehr einen Offset vom Anfang des Strukturfachs von 4 Bytes haben. Kleinere Elemente sind auf ihre eigene Größengrenze auszurichten (z.B. 2 für 'short'). So sieht es aus (das hinzugefügte Byte wird grau dargestellt).
In diesem Fall wird 1 Byte nach dem Mitglied s.c hinzugefügt, so dass das Feld s.s (sizeof(short)==2) die Grenze von 2 Bytes hat (Ausrichtung für den Typ 'short').
Der Offset zum Anfang der Struktur im Array ist ebenfalls auf die 4-Byte-Grenze ausgerichtet, d.h. die Adressen der Elemente a[0], a[1] und a[n] sollen bei Simple_Structure arr[] ein Vielfaches von 4 Bytes sein.
Betrachten wir zwei weitere Strukturen, die aus ähnlichen Typen mit 4-Byte-Ausrichtung, aber mit einer unterschiedlichen Reihenfolge der Mitglieder. In der ersten Struktur befinden sich die Elemente in aufsteigender Reihenfolge der Typengröße.
//+------------------------------------------------------------------+
|
Wie wir sehen können, ist die Strukturgröße 8 und besteht aus den beiden 4-Byte-Blöcken. Der erste Block enthält die Felder mit den Typen 'char' und 'short', während der zweite Block das Feld mit dem Typ 'int' enthält.
Nun verwandeln wir die erste Struktur in die zweite, die sich nur in der Feldreihenfolge unterscheidet, indem wir das Element mit dem Typ 'short' an das Ende verschieben.
//+------------------------------------------------------------------+
|
Obwohl sich der Strukturinhalt nicht geändert hat, hat sich die Größe durch die Änderung der Elementreihenfolge erhöht.
Die Ausrichtung sollte auch bei der Vererbung berücksichtigt werden. Lassen Sie uns dies anhand der einfachen übergeordneten Struktur mit einem einzigen Element vom Typ 'char' demonstrieren. Die Strukturgröße ohne Ausrichtung ist 1.
struct Eltern
|
Erstellen wir die abgeleitete Klasse Kind mit dem Mitglied vom Typ 'short' (sizeof(short)=2).
struct Kind pack(2) : Parent
|
Im Ergebnis ist die Strukturgröße gleich 4, wenn die Ausrichtung auf 2 Bytes festgelegt wird, obwohl die Größe der Elemente 3 ist. In diesem Beispiel sollen der übergeordneten Klasse 2 Bytes zugeordnet werden, so dass der Zugriff auf das Feld 'short' der Unterklasse auf 2 Bytes ausgerichtet ist.
Das Wissen darüber, wie Speicher für die Strukturmitglieder zugeteilt wird, ist notwendig, wenn eine MQL5-Anwendung mit gelesenen/geschriebenen Daten von Dateien oder Streams arbeitet.
Das MQL5\Include\WinAPI-Verzeichnis der Standardbibliothek enthält die Funktionen zum Arbeiten mit den WinAPI-Funktionen. Diese Funktionen wenden die Strukturen mit einer bestimmten Ausrichtung auf die Fälle an, in denen sie für die Arbeit mit WinAPI erforderlich sind.
offsetof ist ein spezieller Befehl, der sich direkt auf das Attribut pack bezieht. Es ermöglicht uns, das Offset eines Elements am Anfang der Struktur zu abzurufen.
//--- Deklaration der Variablen vom Typ Kind
|
Modifikator 'final' #
Das Vorhandensein des Modifikators 'final' bei der Deklaration der Struktur verbietet weitere Vererbung. Wenn es nicht notwendig ist, die Struktur weiter zu ändern oder wenn Änderungen aus Sicherheitsgründen unzulässig sind, deklarieren Sie diese mit dem Modifikator 'final'. Dadurch werden alle Mitglieder der Struktur implizit als endgültig gelten.
struct settings final |
Bei einem Versuch der Vererbung von einer Struktur mit dem Modifikator 'final', wie im Beispiel oben, wirft der Kompiler einen Fehler aus:
cannot inherit from 'settings' as it has been declared as 'final' |
Klassen #
Klassen unterscheiden sich von den Strukturen wie folgt:
- Das Schlüsselwort Klasse wird in der Deklaration verwendet;
- Standardmäßig haben alle Klassenmitglieder Zugriff auf die Spezifikation private, sofern nicht anders angegeben. Datenelemente der Struktur haben die standardmäßig die Zugriffsart 'public', sofern nicht anders angegeben;
- Klassenobjekte haben immer eine Tabelle mit virtuellen Funktionen, auch wenn in der Klasse keine virtuellen Funktionen deklariert sind. Strukturen können keine virtuellen Funktionen haben;
- Der Operator new kann auf Klassenobjekte angewendet werden, aber nicht auf Strukturen;
- Klassen können nur von Klassen abgeleitet werden, Strukturen nur von Strukturen.
Klassen und Strukturen können einen expliziten Konstruktor und Destruktor haben. Wenn Ihr Konstruktor explizit definiert ist, ist die Initialisierung einer Struktur- oder Klassenvariablen mit der Initialisierungssequenz nicht möglich.
Beispiel:
struct trade_settings
|
Konstruktoren und Destruktoren
Konstruktor ist eine spezielle Funktion, die automatisch beim Erstellung von Struktur- oder Klassenobjekt aufgerufen. Er wird normalerweise und für Initialisierung von Klassenglieder verwendet. Weitere werden wir nur über die Klassen reden, während das gleiche gilt für Strukturen, soweit nicht anders angegeben. Der Name eines Konstruktors muss mit dem Klassennamen gleich sein. Der Konstruktor hat keinen Rückgabetyp (Sie können den Typ void angeben).
Definierte Klasseglieder – Zeichenketten, dynamische Arrays und Objekte, die Initialisierung erfordern - werden in jedem Fall initialisiert werden, unabhängig davon, ob es ein Konstruktor gibt.
Jede Klasse kann mehrere Konstruktoren mit einer unterschiedlichen Anzahl von Parametern und Initialisierungsliste haben. Ein Konstruktor, der die Angabe von Parametern erfordert, heißt parametrischer Konstruktor.
Ein Konstruktor ohne Parameter ist ein Standard-Konstruktor. Wenn keine Konstruktoren in einer Klasse deklariert sind, erstellt der Kompiler einen Standard-Konstruktor während der Kompilierung.
//+------------------------------------------------------------------+
|
Sie können einen Konstruktor in der Klassenbeschreibung deklarieren und dann dessen Körper definieren. Beispielsweise können zwei Konstruktoren der Klasse MyDateClass auf folgende Weise definiert werden:
//+------------------------------------------------------------------+
|
Im Standard-Konstruktor werden alle Klassenglieder mit Hilfe der Funktion TimeCurrent() gefüllt. Im parametrischen Konstruktor werden nur die Werte der Stunde übergeben. Die anderen Mitglieder der Klasse (m_year, m_month und m_day) werden automatisch mit dem aktuellen Datum initialisiert.
Der Standard-Konstruktor hat eine besondere Bedeutung bei der Initialisierung eines Arrays von Objekten seiner Klasse. Ein Konstruktor, der für alle Parameter Standardwerte setzt, ist kein Standard-Konstruktor. Wir zeigen dies am Beispiel:
//+------------------------------------------------------------------+
|
Wenn Sie in diesem Beispiel diese Zeile entkommentieren
//CFoo foo_array[3]; // diese Variante kann nicht verwendet werden - es wurde kein Standard-Konstruktor angegeben |
oder
//CFoo foo_dyn_array[]; // diese Variante kann nicht verwendet sein - es wurde kein Standard-Konstruktor angegeben |
dann wird der Kompiler den Fehler "default constructor is not defined" zurückgeben.
Wenn die Klasse einen Konstruktor hat, der vom Benutzer definiert ist, wird der Standard-Konstruktor vom Kompiler nicht generiert. Dies bedeutet, dass, wenn ein parametrischer Konstruktor in einer Klasse deklariert worden ist, aber kein Standard-Konstruktor, kann man die Arrays von Objekten dieser Klasse nicht deklarieren. Der Kompiler für diesen Skript einen Fehler auswerfen:
//+------------------------------------------------------------------+
|
In diesem Beispiel hat die Klasse CFoo einen deklarierten parametrischen Konstruktor - in solchen Fällen erstellt der Kompiler keinen Standard-Konstruktor während der Kompilierung. Gleichzeitig wird bei der Deklaration eines Arrays von Objekten davon ausgegangen, dass alle Objekte automatisch angelegt und initialisiert werden sollen. Bei der automatischen Initialisierung eines Objekts ist es notwendig, einen Standard-Konstruktor aufzurufen, aber da der Standard-Konstruktor nicht explizit deklariert und nicht automatisch vom Kompiler generiert wird, ist es unmöglich, ein solches Objekt zu erstellen. Aus diesem Grund erzeugt der Kompiler bei der Kompilierung einen Fehler.
Es gibt eine spezielle Syntax, um Objekte durch dem Konstruktor zu initialisieren. Konstruktor-Initialisierer (Sonderkonstruktionen zur Initialisierung) für die Mitglieder einer Struktur oder Klasse können in der Initialisierungsliste angegeben werden.
Eine Initialisierungsliste ist eine durch Kommata getrennte Liste von Initialisatoren, die nach dem Doppelpunkt nach der Liste der Parameter eines Konstruktors steht und dem Körper vorangestellt wird (steht vor einer öffnenden Klammer). Es gibt mehrere Anforderungen:
- Initialisierungslisten können nur in Konstruktoren verwendet werden.
- Mitglieder von Eltern können nicht in der Initialisierungsliste initialisiert werden
- Nach der Initialisierungsliste muss Definition (Implementierung) der Funktion folgen.
Hier ist ein Beispiel für mehrere Konstruktoren zur Initialisierung von Klassenmitgliedern.
//+------------------------------------------------------------------+
|
In diesem Fall hat die Klasse CPerson drei Konstruktoren:
- Einen expliziten Standard-Konstruktor, der das Erstellen eines Arrays von Objekten dieser Klasse ermöglicht.
- Ein Konstruktor mit einem Parameter, der einen vollständigen Namen als Parameter erhält und ihn entsprechend der gefundenen Anordnung in den Namen und Vornamen teilt;
- Ein Konstruktor mit zwei Parametern, der Initialisierungsliste enthält. Initialisieren – m_second_name(surname) und m_first_name(name).
Beachten Sie, wie die Initialisierung mit einer Liste die Zuweisung ersetzt. Die einzelnen Mitglieder werden wie folgt initialisiert:
Klassenglieder (Liste von Ausdrücken) |
In der Initialisierungsliste können die Mitglieder in beliebiger Reihenfolge stehen, aber alle Mitglieder der Klasse werden nach der Reihenfolge der Deklaration initialisiert. Dies bedeutet, dass im dritten Konstruktor zuerst das Mitglied m_first_name initialisiert, da es an erster Stelle steht, und danach m_second_name. Dies sollte in Fällen berücksichtigt werden, in denen die Initialisierung von einigen Klassenmitgliedern von den Werten der anderen Klassenmitglieder abhängt.
Wenn kein Standard-Konstruktor in der Basisklasse deklariert ist, und ein oder mehrere Konstruktoren mit Parametern deklariert sind, sollten Sie immer einen der Basisklassenkonstruktoren in der Initialisierungsliste anrufen. Sie ist eine Kommata getrennte, reguläre Mitgliederliste und wird bei der Initialisierung des Objekts unabhängig von der Position in der Initialisierungsliste zuerst aufgerufen.
//+------------------------------------------------------------------+
|
In diesem Beispiel während der Erstellung des Objekts bar wird Standard-Konstruktor CBar() aufgerufen. In diesem Standard-Konstruktor erst wird Konstruktor für den Vorfahr CFoo, dann wird Konstruktor für Klassenglied m_member augerufen.
Destruktor ist eine Sonderfunktion, die authomatisch aufgerufen wird, wenn das Klassenobjekt beseitigt wird. Destruktorname wird so geschrieben, wie der Klassenname mit Tilde(~). Zeilen, dynamische Felde und Objekte, die initialisiert werden müssen, werden auf jeden Fall deinitialisiert, unabhängig von der Anwesenheit des Destruktors. Wenn es einen Destruktor gibt, werden diese Handlungen nach Destruktoraufruf durchgeführt werden.
Destruktoren sind immer virtuell, unabhängig davon, ob sie mit dem Schlüuesselwort virtual erklärt werden oder nicht.
Methodenbestimmung der Klassen
Funktionen-Methoden der Klasse können sowohl innerhalb, als auch ausserhalb der Klassenerklärung bestimmt werden. Wenn die Methode innerhalb der Klasse bestimmt wird, folgt Ihr Körper unmittelbar nach der Methodenerklärung.
Beispiel:
class CTetrisShape |
Funktionen mit SetRightBorder(int border) nach Draw() werden vereinbart und bestimmt innerhalb der Klasse CTetrisShape.
Konstruktor CTetrisShape() und Methoden CheckDown(int& pad_array[]), CheckLeft(int& side_row[]) und CheckRight(int& side_row[]) werden nur innerhalb der Klasse erklärt, aber noch nicht bestimmt. Bestimmungen dieser Funktionen müssen weiter nach Kode folgen. Um die Methode ausser der Klasse zu bestimmen, wird Operation der Kontexterlaubnis verwendet , als Kontext funktioniert der Klassenname.
Beispiel:
//+------------------------------------------------------------------+ |
Zugangsmodifikatoren public, protected und private
Bei der Ausarbeitung der neuen Klasse ist es empfohlen, den Zugang zu Aussengliedern zu begrenzen. Für diese Zwecke werden Schlüsselwörter private oder protected verwendet. In diesem Fall wird der Zugang zu diesen versteckten Daten nur aus Funktionen-Methoden derselben Klasse erfolgen. Wenn das Schluesselwort protected verwendet wird, kann der Zugang zu versteckten Daten auch aus Methoden der Klassen erfolgen - Nachfolger dieser Klasse. In gleicher Weise kann der Zugang auch zu Funktionen-Metoden der Klasse begrenzt werden.
Wenn es notwendig ist, den Zugang zu Mitgliedern und/oder Methoden der Klasse vollkommen zu eröffnen, wird das Schlüsselwort publicverwendet.
Beispiel:
class CTetrisField
|
Alle Mitglieder und Methoden der Klasse, definiert nach dem Spezifikator public: (und bis nächsten Spezifikator), sind für jeden Zugriff auf ein Objekt dieser Klasse zugänglich. In diesem Beispiel sind es folgende Mitglieder: Die Funktionen CTetrisField(), Init(), Deinit(), Down(), Left(), Right(), Rotate() und Drop().
Alle Mitglieder der Klasse, deklariert nach dem Spezifikator private: (und bis nächsten Spezifikator) sind nur für die Funktions-Mitglieder dieser Klasse zugänglich. Spezifikatoren für einen Zugriff auf die Elemente werden immer mit einem Doppelpunkt (:) und können in der Klassenbestimmung mehrmals auftreten.
Alle Klassenmitglieder, die nach 'protected' deklariert wurden: Zugriffsspezifikation (und bis zum nächsten Zugriffsspezifikation) sind nur für Mitgliederfunktionen dieser Klasse und Mitgliederfunktionen der abgeleiteten Klassen verfügbar. Bei einem versuch auf die Mitglieder zu zuzugreifen, die innerhalb der Spezifikatoren 'private' oder 'protected' deklariert wurden, erhalten wir einen Fehler bei der Kompilierung. Beispiel:
class A
|
Beim Kompilieren des Codes wird eine Fehlermeldung ausgeworfen — ein Versuch, die ein Objekt auf anderes zu kopieren:
attempting to reference deleted function 'void B::operator=(const B&)' trash3.mq5 32 6 |
Die zweite Zeile unten bietet eine detailliertere Beschreibung — der Kopieroperator in Klasse B wurde explizit gelöscht, da der nicht verfügbare Kopieroperator der Klasse A aufgerufen wird:
function 'void B::operator=(const B&)' was implicitly deleted because it invokes inaccessible function 'void A::operator=(const A&)' |
Der Zugriff auf die Mitglieder der Basisklasse kann während Vererbung in abgeleiteten Klassen neu definiert werden.
Zugang zu Gliedern der Basisklasse kann bei Vererbung in abgeleiteten Klassen neu definiert werden.
Der Spezifikator delete markiert Funktionen einer Klasse, die nicht verwendet werden können. Das heißt, wenn das Programm explizit oder implizit auf eine solche Funktion verweist, wird der Fehler bereits bei der Kompilierung ausgeworfen. Mit diesem Spezifikator können Sie beispielsweise Methoden der Eltern in einer Unterklasse nicht verfügbar machen. Das gleiche Ergebnis kann erreicht werden, wenn wir die Funktion im privaten Bereich der Elternklasse deklarieren (Deklarationen im Abschnitt private). Hier macht die Verwendung von delete den Code auf der Ebene der Nachkommen lesbarer und verwaltbarer.
class A
|
Die Kompilernachricht
attempting to reference deleted function 'double B::GetValue()'
|
Der Spezifikator 'delete' erlaubt es, das Auto-Casting oder den Kopierkonstruktor zu deaktivieren, die sonst auch im Abschnitt 'private' versteckt werden müssten. Beispiel:
class A
|
Während der Kompilierung erhalten wir die Fehlermeldungen:
attempting to reference deleted function 'void A::SetValue(int)'
|
Modifikator 'final' #
Das Vorhandensein des Modifikators 'final' bei der Deklaration der Struktur verbietet weitere Vererbung. Wenn es nicht notwendig ist, die Struktur weiter zu ändern oder wenn Änderungen aus Sicherheitsgründen unzulässig sind, deklarieren Sie diese mit dem Modifikator 'final'. Dabei werden alle Glieder der Struktur implizit auch als final gelten.
class CFoo final |
Bei einem Versuch der Vererbung von einer Struktur mit dem Modifikator 'final', wie im Beispiel oben, wirft der Kompiler einen Fehler aus:
cannot inherit from 'CFoo' as it has been declared as 'final' |
Unions (union) #
Eine Union stellt einen besonderen Datentyp dar und setzt sich aus mehreren Variablen zusammen, die sich denselben Speicherbereich teilen. Daraus folgt, dass eine Union die Interpretation einer und derselben Reihenfolge der Bytes auf zwei (oder mehr) verschiedenen Weisen ermöglicht. Die Deklaration einer Union ist gleich der Deklaration einer Struktur und beginnt mit dem Schlüsselwort union.
union LongDouble
|
Im Gegensatz zu Strukturen, gehören verschiedene Komponenten einer Union zu demselben Speicherbereich. In diesem Beispiel wurde die LongDouble Union deklariert, in welcher sich der Wert vom Typ long und der Wert vom Typ double einen Speicherbereich teilen. Es ist wichtig zu verstehen, die Union kann nicht gleichzeitig den ganzzahligen Wert long und den reellen Wert double speichern (wie es in Strukturen der Fall ist), denn die Variablen long_value und double_value überschneiden sich (im Speicher). Das MQL5-Programm kann aber jederzeit die Information, die in der Union enthalten ist, als einen ganzzahligen (long) oder reellen Wert (double) bearbeiten. Daraus folgt, dass die Union die Interpretation einer und derselben Reihenfolge von Daten auf zwei (oder mehr) verschiedenen Weisen ermöglicht.
Bei der Deklaration der Union reserviert der Compiler einen Speicherbereich, der für das Speichern des größten nach dem Volumen Typs in der Union der Variablen ausreichend ist. Für den Zugang zu einem Element der Union wird dieselbe Syntax wie für Strukturen verwendet – der Point-Operator.
union LongDouble
|
Da Unions es dem Programm erlauben, dieselben Daten im Speicher unterschiedlich zu interpretieren, werden sie häufig in den Fällen verwendet, wenn eine ungewöhnliche Typumwandlung benötigt wird.
Auf Unions kann keine Vererbung angewendet werden und sie können per Definition keine statischen Members haben. Sonst verhält sich union wie eine Struktur, deren Mitglieder eine Nullpunktverschiebung haben. Die folgenden Typen können nicht Mitglieder einer Union sein:
- dynamische Arrays
- Strings
- Pointer auf Objekte und Funktionen
- Objekte von Klassen
- Objekte der Strukturen, die Konstruktoren und Destruktoren haben
- Objekte der Strukturen, die Mitglieder der Punkte 1-5 beinhalten
Eine Union kann, so wie Klassen, Konstruktoren und Destruktoren und Methoden haben. Standardmäßig haben die Members einer Union den Zugriffstyp public, für die Erstellung privater Elemente ist das Schlüsselwort private zu verwenden. Alle diese Möglichkeiten sind in einem Beispiel vorgestellt. Das Beispiel zeigt, wie man die Farbe mit dem Typ color in ARGB umwandeln kann, wie dies die ColorToARGB() Funktion macht.
//+------------------------------------------------------------------+
|
Schnittstellen (Interfaces) #
Eine Schnittstelle gibt an, welche Funktionalität eine Klasse dann umsetzen kann. In der Tat ist sie eine Klasse, die keine Mitglieder enthalten kann und keinen Konstruktor und/oder Destruktor hat. Alle in einer Schnittstelle deklarierten Methoden sind rein virtuell, auch ohne eine explizite Definition.
Eine Schnittstelle wird mit dem Schlüsselwort interface definiert. Zum Beispiel:
//--- Basisschnittstelle für die Beschreibung von Tieren
|
Wie im Fall mit abstrakte Klassen, kann man nicht eine Objekt-Schnittstelle ohne Vererbung erstellen. Die Schnittstelle kann nur von anderen Schnittstellen vererbt werden, und auch kann Eltern der Klasse sein. Eine Schnittstelle hat immer die öffentliche Sichtbarkeit.
Die Schnittstelle kann nicht innerhalb einer Klassendeklaration oder Strukturdeklaration deklariert werden, aber man kann den Zeiger auf die Schnittstelle in einer Variablen vom Typ void * speichern. Im Allgemeinen können Sie einen Zeiger auf ein Objekt einer beliebigen Klasse in der Variable vom Typ void * speichern. Um einen void * Zeiger auf einen Zeiger auf ein Objekt einer bestimmten Klasse umzuwandeln, wird der Operator dynamic_cast verwendet. Wenn die Umwandlung nicht möglich ist, wird das Ergebnis der Operation dynamic_cast NULL.
Siehe auch