Domande su OOP (programmazione orientata agli oggetti) - pagina 6

 
C-4:
I puntatori sono indispensabili per conversioni complesse di oggetti dove è necessaria l'identificazione dinamica del tipo.

Vasily, un esempio, per favore!

Conosco solo un caso in cui dovete allocare la memoria e avete bisogno di un puntatore ad essa.

Sono sicuro che puoi quasi sempre farne a meno. È auspicabile non usare la gestione manuale della memoria. C'è sempre una libreria standard che gestisce già questi problemi.

 
Integer:


Un principio si applica ovunque: tutto deve essere fatto nel modo più semplice possibile. Non c'è bisogno di entrare nel vivo delle cose solo per essere nel vivo delle cose. Se un problema può essere risolto semplicemente, dovrebbe essere risolto semplicemente.


Esattamente. Ed è qui che è importante progettare correttamente la classe. E per fare questo, è importante prevedere quali saranno le prossime classi (discendenti).

Di solito, la classe base dovrebbe avere una funzionalità minima, ma è auspicabile creare molti metodi virtuali che fissino il vettore di utilizzo della classe.

Nel caso di MQL - probabilmente il contrario - più funzionalità nella classe base, come in VOLDEMAR. Ma senza fanatismo.


Ci sono proprietà di simboli commerciali (POINT, STOPLEVEL ecc.). MODE_XXX per MarketInfo() ). Sarebbe meglio spingerli nella classe cSymbol, per esempio.


Ci sono proprietà dell'ordine (prezzo di apertura, tipo, lotto, ecc.). OrderXXX() ). Sarebbe meglio metterli in una classe cOrder separata, per esempio.

Se ricordiamo che l'ordine può essere chiuso in parti, allora dovremmo anche assegnare il campo BaseLot alla classe (per sapere quale parte del lotto è già stata chiusa), ecc.


C'è (per me) un programma - determinati periodi di tempo (per giorno) in cui possiamo aprire e chiudere ordini, periodi in cui chiudiamo solo (trailing se abbiamo già breakeven) e il periodo in cui non lavoriamo affatto e quindi chiudiamo tutti gli ordini aperti e cancelliamo quelli non aperti.

Ricordatevi delle lacune e che nell'ultima ora di trading in molte (se non tutte) le società di brokeraggio possono cambiare le condizioni di trading e che molte società di brokeraggio hanno una pausa nei metalli di trading ogni giorno di trading.


E infine, c'è un EA - un certo algoritmo che lavora con un certo simbolo, su un certo programma, con una certa lista di "suoi" ordini e con i propri dati per il calcolo.

Possiamo creare una classe cExpert, che conterrà un oggetto della classe cSymbol e un array di oggetti della classe cOrder. Questo cExpert di base conterrà funzioni che aggiornano le proprietà degli oggetti cOrder, funzioni di gestione degli ordini, gestione degli errori, statistiche, ecc.

Personalmente trovo utili le funzioni di calcolo dei lotti in % di AccountFreeMargin(), la generazione di un wizard unico per ogni ordine (più facile mettere un ordine inverso a un ordine specifico), la funzione di mettere un ordine inverso, ecc.

Ed è da qui che potrebbe discendere cExpert , con insiemi unici di dati aggiuntivi e funzioni che deciderebbero cosa fare sul mercato (ordini di apertura/compagnia/chiusura).

Cioè implementare strategie di trading. E sarà quello che chiamiamo un "EA a strategia XXX".


Ma TUTTE le funzionalità di base saranno nella classe base cExpert. Le classi figlio conterranno algoritmi di strategie di trading.

Beh, forse qualcuno aggiungerà qualche gestione avanzata degli errori o statistiche avanzate sul trading (se non c'è codice sorgente e non può essere stipato in cExpert di base).

Si prega di notare che stiamo prendendo il nostro tempo e creando una classe che semplificherà la scrittura degli EA. In effetti, è un modello di Expert Advisor. Strutturalmente completo e non spazzolato (tutti i dati necessari in un unico posto con il codice).


Per quanto riguarda la presunta "ridondanza" delle funzioni wrapper su openorders(), personalmente sostengo solo tali wrapper (specialmente - se hanno funzionalità extra, e non solo chiamano la funzione di base).

Per esempio, se in una chiamata di wrapper SL e TP sono specificati in punti e dobbiamo convertirli in cifre assolute, e inoltre aggiungere o sottrarre a seconda del tipo di ordine (sebbene anche in questo caso tali conversioni possano essere messe in operorders()).

Personalmente trovo più facile capire il codice che chiama BuyStop(...) e controllare una volta che BuyStop() faccia tutto correttamente piuttosto che analizzare ogni volta i parametri di OrderSend() per verificarne la correttezza.

PS: Era un sacco di lavoro, anche nei fine settimana. Grazie a Pavlick e mql5 per i codici di esempio.

Date un'occhiata a questi esempi e pensate, è davvero necessario creare una gerarchia di classi nell'area dei compiti di programmazione, che sono scritti in MQL?

Avete davvero bisogno di un'intera famiglia di classi basate su una base (tutti quei "triangoli", "quadrati" ecc.)?

E ancora una volta le parole d'oro di Integer: non andare nel boschetto solo per essere nel boschetto.

O non l'ho notato, o la sezione della documentazione con gli esempi è apparsa di recente.

 
Zhunko:

Conosco solo un caso in cui dovete allocare la memoria e avete bisogno di un puntatore ad essa.

Sono sicuro che puoi quasi sempre farne a meno. È consigliabile non usare la gestione manuale della memoria. C'è sempre una libreria standard che ha già risolto questi problemi.


Ecco probabilmente di cosa si tratta.


Supponiamo che ci sia una classe cFather, ha il metodo int GetData() che restituisce 3. E il metodo PrintData(), che emette ciò che ottiene da GetData().

C'è il suo discendente cChild che ha sovrascritto GetData() che ora restituisce 5.

Se dichiariamo un oggetto di cFather di tipo TestObject, allora TestObject.GetData() restituirà sempre 3.

Se dichiariamo cFather* TestObject=new cChild1, allora TestObject.GetData() restituirà 5, anche se sembra essere cFather.

È necessario affinché il codice scritto ora possa chiamare il metodo GetData() su qualsiasi discendente della classe cFather, anche se questa (classe discendente) non esiste ancora.

Cioè, se poi appare la classe cChild2, con la quale GetData() restituirà 7, allora dopo cFather* Test2=new cChild2 la funzione Test2.PrintData() inizierà ad emettere 7.

Se c'è qualche funzione che si aspetta un parametro "riferimento all'oggetto di classe cFather" e lo usa GetData(), otterrà i dati corretti per qualsiasi discendente di cFather.

Il binding del metodo avviene quando viene chiamato new. Se non è referenziata, allora il binding sarà duro, cioè saranno chiamati i metodi della classe dichiarata.

Vedi qui e qui

 
EverAlex:

Sembra che si tratti di questo.

...

C'è un operatore "::" che permette di accedere a qualsiasi metodo nell'intera catena di classi base e derivate senza allocare memoria o un puntatore.
 
C-4:


La vostra classe è ridondante al 90%. Solo due funzioni fanno il lavoro principale, queste sono openorders e tip Perché usate Sel, Buy SelStop, etc., quando in realtà tutte chiamano semplicemente Openorders? Inoltre, il tipo di ordine è passato come int, quindi non è protetto. Invece di int è meglio che usiate la vostra enumerazione o lo standard ENUM_ORDER_TYPE. E in generale, è meglio non usare mai i numeri magici "1", "2" ecc. Questo vi impedirà di inviare il valore dell'ordine di sinistra alla funzione. La stessa funzione Openorders è troppo grande. Ovviamente, consiste di due blocchi, il blocco di fare un accordo e il blocco di verificare le condizioni. Ognuno di essi dovrebbe essere una funzione privata separata.

È un buon inizio, ma abbiamo ancora molto da imparare. La funzione di punta sarebbe meglio riscritta come segue:

È comodo per me vedere visivamente che tipo di ordine sto piazzando quando chiamo il metodo...

Riguardo al confronto, descrivi in dettaglio perché non è raccomandato confrontare il doppio con 0?

 
C-4:
I puntatori sono indispensabili per le conversioni di oggetti complessi in cui è necessaria l'identificazione dinamica del tipo.

La presenza di un'identificazione dinamica del tipo indica di solito l'architettura a stampella di un progetto.
 
se una classe è dichiarata globalmente in un EA (Classe c;) gli stati degli oggetti interni della classe cambiati in un tick saranno salvati quando arriva il tick successivo?
 
EverAlex:

Sembra che si tratti di questo.


Supponiamo che ci sia la classe cFather; ha il metodo int GetData() che restituisce 3. E il metodo PrintData(), che emette ciò che ottiene da GetData().

C'è il suo discendente cChild, che ha sovrascritto GetData(), che ora restituisce 5.

Se dichiariamo un oggetto di cFather di tipo TestObject, allora TestObject.GetData() restituirà sempre 3.

Se dichiariamo cFather* TestObject=new cChild1, allora TestObject.GetData() restituirà 5, anche se sembra essere cFather.

È necessario affinché il codice scritto ora possa chiamare il metodo GetData() su qualsiasi discendente della classe cFather, anche se questa (classe discendente) non esiste ancora.

Cioè, se poi appare la classe cChild2, con la quale GetData() restituirà 7, allora dopo cFather* Test2=new cChild2 la funzione Test2.PrintData() inizierà ad emettere 7.

Se c'è qualche funzione che si aspetta un parametro "riferimento all'oggetto di classe cFather" e lo usa GetData(), otterrà dati corretti per qualsiasi discendente di cFather.

Il binding del metodo avviene quando viene chiamato new. Se non è referenziato, allora il binding sarà duro, cioè saranno chiamati i metodi della classe dichiarata.

Vedi qui e qui

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:


Forse ho scritto l'esempio per niente. Non funziona in MKL:

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

H.k., non puntatori, ma risate su un bastone.

P.S: scrivere in dll, c'è la possibilità di imparare un linguaggio normale.

 
Pavlick:


Forse ho scritto l'esempio per niente. Non funziona in MKL:

H.k., non puntatori, ma risate su un bastone.

P.S: scrivere in dll, c'è la possibilità di imparare un linguaggio normale.


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;
  }
Motivazione: