Fragen zu OOP (Objektorientierte Programmierung) - Seite 6

 
C-4:
Zeiger sind unverzichtbar für komplexe Objektkonvertierungen, bei denen eine dynamische Typidentifizierung erforderlich ist.

Wassili, ein Beispiel, bitte!

Mir ist nur ein Fall bekannt, in dem Sie Speicher zuweisen müssen und einen Zeiger darauf benötigen.

Ich bin sicher, Sie können fast immer darauf verzichten. Es ist wünschenswert, keine manuelle Speicherverwaltung zu verwenden. Es gibt immer eine Standardbibliothek, die diese Probleme bereits behandelt.

 
Integer:


Ein Grundsatz gilt überall: Alles muss so einfach wie möglich sein. Man muss sich nicht ins Dickicht begeben, nur weil man im Dickicht sein will. Wenn ein Problem einfach gelöst werden kann, sollte es auch einfach gelöst werden.


Ganz genau. Und genau hier ist es wichtig, die Klasse richtig zu gestalten. Dazu ist es wichtig, vorherzusagen, wie die nächsten Klassen (Nachkommen) aussehen werden.

In der Regel sollte die Basisklasse eine Mindestfunktionalität haben, aber es ist wünschenswert, viele virtuelle Methoden zu erstellen, die den Vektor der Klassennutzung festlegen würden.

Im Falle von MQL - wahrscheinlich das Gegenteil - mehr Funktionalität in der Basisklasse, wie in VOLDEMAR. Aber ohne Fanatismus.


Es gibt Handelssymboleigenschaften (POINT, STOPLEVEL usw.). MODE_XXX für MarketInfo() ). Wir sollten sie zum Beispiel besser in die Klasse cSymbol verschieben.


Es gibt Auftragseigenschaften (Eröffnungskurs, Typ, Los, etc.). BestellungXXX() ). Es wäre besser, sie z. B. in eine eigene Klasse cOrder aufzunehmen.

Wenn wir daran denken, dass der Auftrag in Teilen abgeschlossen werden kann, dann sollten wir der Klasse auch das Feld BaseLot zuweisen (um zu wissen, welcher Teil des Loses bereits abgeschlossen wurde) usw.


Es gibt (für mich) einen Zeitplan - bestimmte Zeiträume (pro Tag), in denen wir Aufträge öffnen und schließen können, Zeiträume, in denen wir nur schließen (nachlaufend, wenn wir bereits die Gewinnschwelle erreicht haben) und einen Zeitraum, in dem wir überhaupt nicht arbeiten und dann alle offenen Aufträge schließen und nicht geöffnete löschen.

Denken Sie an die Lücken und daran, dass viele (wenn nicht alle) Maklerunternehmen in der letzten Handelsstunde die Handelsbedingungen ändern können und dass viele Maklerunternehmen an jedem Handelstag eine Pause im Metallhandel machen.


Und schließlich gibt es einen EA - einen bestimmten Algorithmus, der mit einem bestimmten Symbol, nach einem bestimmten Zeitplan, mit einer bestimmten Liste von "seinen" Aufträgen und mit seinen eigenen Daten zur Berechnung arbeitet.

Wir können eine Klasse cExpert erstellen, die ein Objekt der Klasse cSymbol und eine Reihe von Objekten der Klasse cOrder enthält. Dieser grundlegende cExpert enthält Funktionen zur Aktualisierung der Eigenschaften von cOrder-Objekten, Funktionen zur Auftragsabwicklung, Fehlerbehandlung, Statistik usw.

Ich persönlich finde die Funktionen der Losberechnung in %% von AccountFreeMargin(), die Generierung eines eindeutigen Assistenten für jeden Auftrag (einfacher, einen umgekehrten Auftrag zu einem bestimmten Auftrag zu erteilen), die Funktion, einen umgekehrten Auftrag zu erteilen, usw. nützlich.

Und hier könnte cExpert mit einzigartigen zusätzlichen Datensätzen und Funktionen ansetzen, die entscheiden, was auf dem Markt zu tun ist (Aufträge öffnen/begleiten/schließen).

D.h. Handelsstrategien umsetzen. Und es wird das sein, was wir einen "XXX-Strategie-EA" nennen.


Aber ALLE Basisfunktionen werden in der Basisklasse cExpert enthalten sein. Die untergeordneten Klassen werden Algorithmen für Handelsstrategien enthalten.

Nun, vielleicht wird jemand eine erweiterte Fehlerbehandlung oder erweiterte Statistiken über den Handel hinzufügen (wenn es keinen Quellcode gibt und es nicht in cExpert eingepfercht werden kann).

Bitte beachten Sie, dass wir uns Zeit nehmen und eine Klasse erstellen, die das Schreiben von EAs vereinfacht. Es handelt sich dabei um eine Expert Advisor-Vorlage. Strukturell vollständig und nicht gebürstet (alle erforderlichen Daten an einem Ort mit dem Code).


Was die angebliche "Redundanz" von Wrapper-Funktionen gegenüber openorders() angeht, so unterstütze ich persönlich solche Wrapper (vor allem, wenn sie zusätzliche Funktionen bieten und nicht nur einen Aufruf einer Basisfunktion).

Zum Beispiel, wenn in einem Wrapper-Aufruf SL und TP in Punkten angegeben werden und wir sie in absolute Ziffern umwandeln müssen, und außerdem je nach Ordnungstyp addieren oder subtrahieren müssen (obwohl auch in diesem Fall solche Umwandlungen in operorders() platziert werden können).

Ich persönlich finde es einfacher, den Code zu verstehen, der BuyStop(...) aufruft, und einmal zu prüfen, ob BuyStop() alles richtig macht, als jedes Mal die OrderSend() -Parameter auf ihre Korrektheit zu überprüfen.

PS: Es war eine Menge Arbeit, auch an den Wochenenden. Vielen Dank an Pavlick und mql5 für die Beispielcodes.

Schauen Sie sich diese Beispiele an und überlegen Sie, ob es wirklich notwendig ist, eine Hierarchie von Klassen im Bereich der Programmieraufgaben zu erstellen, die in MQL geschrieben sind?

Braucht man wirklich eine ganze Familie von Klassen, die auf einer Grundklasse basieren (all diese "Dreiecke", "Quadrate" usw.)?

Und wieder einmal die goldenen Worte von Integer: Geh nicht ins Dickicht, nur um im Dickicht zu sein.

Entweder habe ich es nicht bemerkt, oder der Abschnitt mit den Beispielen ist erst kürzlich erschienen.

 
Zhunko:

Mir ist nur ein Fall bekannt, in dem Sie Speicher zuweisen müssen und einen Zeiger darauf benötigen.

Ich bin sicher, Sie können fast immer darauf verzichten. Es ist nicht ratsam, die Speicherverwaltung manuell vorzunehmen. Es gibt immer eine Standardbibliothek, die diese Probleme bereits gelöst hat.


Wahrscheinlich geht es um Folgendes.


Nehmen wir an, es gibt eine Klasse cFather, die die Methode int GetData() hat, die 3 zurückgibt. Und die Methode PrintData(), die ausgibt, was sie von GetData() erhält.

Sein Nachkomme cChild hat GetData() überschrieben und gibt nun 5 zurück.

Wenn wir ein Objekt des cFather-Typs TestObject deklarieren, wird TestObject.GetData() immer 3 zurückgeben.

Wenn wir cFather* TestObject=new cChild1 deklarieren, dann wird TestObject.GetData() 5 zurückgeben, obwohl es cFather zu sein scheint.

Sie wird benötigt, damit der jetzt geschriebene Code die Methode GetData() für jeden Nachkommen der Klasse cFather aufrufen kann, auch wenn diese (Nachkommenklasse) noch nicht existiert.

Das heißt, wenn dann die Klasse cChild2 erscheint, bei der GetData() 7 zurückgibt, dann wird nach cFather* Test2=new cChild2 function Test2.PrintData() anfangen, 7 auszugeben.

Wenn es eine Funktion gibt, die einen "Verweis auf das Klassenobjekt cFather" als Parameter erwartet und diese Funktion GetData() verwendet, erhält sie die korrekten Daten für jeden Nachkommen von cFather.

Die Methodenbindung erfolgt beim Aufruf von new. Wenn sie nicht referenziert wird, ist die Bindung hart, d.h. es werden die Methoden der deklarierten Klasse aufgerufen.

Siehe hier und hier

 
EverAlex:

Genau darum scheint es zu gehen.

...

Es gibt einen "::"-Operator, mit dem Sie auf jede Methode in der gesamten Kette von Basis- und abgeleiteten Klassen zugreifen können, ohne Speicher oder einen Zeiger zuzuweisen.
 
C-4:


Ihre Klasse ist zu 90 % überflüssig. Nur zwei Funktionen machen die Hauptarbeit, das sind Openorders und tip Warum verwenden Sie Sel, Buy SelStop usw., wenn sie doch alle nur Openorders aufrufen? Außerdem wird der Auftragstyp als int übergeben, ist also nicht geschützt. Anstelle von int sollten Sie entweder Ihre eigene Aufzählung oder den Standard ENUM_ORDER_TYPE verwenden. Und im Allgemeinen sollten Sie besser nie die magischen Zahlen "1", "2" usw. verwenden, sondern nur Aufzählungen. Dadurch wird verhindert, dass Sie den linken Bestellwert an die Funktion senden. Die Funktion Openorders selbst ist zu groß. Sie besteht natürlich aus zwei Blöcken, dem Block der Geschäftsabschlüsse und dem Block der Überprüfung der Bedingungen. Jede von ihnen sollte eine separate private Funktion sein.

Das ist ein guter Anfang, aber wir haben noch viel zu lernen. Die Tippfunktion lässt sich besser wie folgt umschreiben:

Es ist praktisch für mich, wenn ich beim Aufruf der Methode sehen kann, welche Art von Bestellung ich aufgebe...

Beschreiben Sie bitte ausführlich, warum es nicht empfehlenswert ist, double mit 0 zu vergleichen?

 
C-4:
Zeiger sind unverzichtbar für komplexe Objektkonvertierungen, bei denen eine dynamische Typidentifizierung erforderlich ist.

Das Vorhandensein einer dynamischen Typenidentifikation weist in der Regel auf die Krückenarchitektur eines Projekts hin.
 
Wenn eine Klasse in einem EA global deklariert wird (Klasse c;), werden dann die Zustände der internen Klassenobjekte, die bei einem Tick geändert werden, beim nächsten Tick gespeichert?
 
EverAlex:

Genau darum scheint es zu gehen.


Angenommen, es gibt die Klasse cFather; sie hat die Methode int GetData(), die 3 zurückgibt. Und die Methode PrintData(), die ausgibt, was sie von GetData() erhält.

Es gibt seinen Nachkommen cChild, der GetData() überschrieben hat und nun 5 zurückgibt.

Wenn wir ein Objekt des cFather-Typs TestObject deklarieren, wird TestObject.GetData() immer 3 zurückgeben.

Wenn wir cFather* TestObject=new cChild1 deklarieren, dann wird TestObject.GetData() 5 zurückgeben, obwohl es cFather zu sein scheint.

Sie wird benötigt, damit der jetzt geschriebene Code die Methode GetData() für jeden Nachkommen der Klasse cFather aufrufen kann, auch wenn diese (Nachkommenklasse) noch nicht existiert.

Das heißt, wenn dann die Klasse cChild2 erscheint, bei der GetData() 7 zurückgibt, dann wird nach cFather* Test2=new cChild2 function Test2.PrintData() anfangen, 7 auszugeben.

Wenn es eine Funktion gibt, die einen Parameter "Verweis auf das Klassenobjekt cFather" erwartet und diese Funktion GetData() verwendet, erhält sie korrekte Daten für jeden cFather-Nachkommen.

Die Methodenbindung erfolgt beim Aufruf von new. Wenn sie nicht referenziert wird, ist die Bindung hart, d.h. es werden die Methoden der deklarierten Klasse aufgerufen.

Siehe hier und hier

class cFather
{
public:
    int GetData() {return 3;}
};

class cChild : public cFather
{
public:
    int GetData() {return 5;}
};
    
int f(cFather *p) {return p->GetData();}
    
int main()
{
    cChild obj;
    f(&obj);                // вернет 3
    obj.cFather::GetData(); // вернет 3
    
    return 0;
}
 
Pavlick:


Vielleicht habe ich das Beispiel umsonst geschrieben. In MKL funktioniert das nicht:

cChild obj;
f(&obj);                // вернет 3
obj.cFather::GetData(); // вернет 3

H.k., keine Pointen, sondern Lachen am Stiel.

P.S.: Schreiben Sie in dlls, dann haben Sie die Möglichkeit, eine normale Sprache zu lernen.

 
Pavlick:


Vielleicht habe ich das Beispiel umsonst geschrieben. In MKL funktioniert das nicht:

H.k., keine Pointen, sondern Lachen am Stiel.

P.S.: Schreiben Sie in dlls, so haben Sie die Möglichkeit, eine normale Sprache zu lernen.


class cFather
  {
public:
   int GetData() {return 3;}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class cChild : public cFather
  {
public:
   int GetData() {return 5;}
  };

int f(cFather *p) {return p.GetData();}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnStart()
  {
   cChild obj,*ptr=GetPointer(obj);
   f(ptr);                     // вернет 3
   ((cFather *)ptr).GetData(); // вернет 3

   return 0;
  }