Domande su OOP in MQL5 - pagina 45

 
Dmitry Fedoseev:

Qualcuno può spiegare come questa inizializzazione dei campi sia migliore di questa:

è meglio di questo:

E comunque qual è il punto?

I campi const possono essere inizializzati.

 
Dmitry Fedoseev:

Qualcuno può spiegare come questa inizializzazione dei campi sia migliore di questa:

è meglio di questo:

E comunque qual è il punto?

Nel primo caso si ha l'inizializzazione, e nel secondo caso - le operazioni vengono eseguite per i campi inizializzati di default. Quindi tutto dipende dal compilatore, o non fa alcuna differenza, o un casino di azioni extra in run-time. Nei plus, la seconda variante è considerata una cattiva forma.
 
L'operatore =, come dovrebbe, restituisce il valore r (5<(val=7) funziona), ma proprio con la semantica carry, qui non c'è, ed è stato richiesto, purtroppo, quindi non c'è nemmeno un costruttore carry.
 
Igor Makanu:

la questione è puramente teorica:

hanno visto, forse in SB, una chiamata al costruttore come questa:

come sarebbe diverso questo codice:

L'ho decompresso, non vedo alcuna differenza, allora sii un po' più specifico - cosa o perché possiamo usare una chiamata forzata al costruttore per gli oggetti a1 e a2?

qual è la "convenienza" della prima opzione?

La prima opzione si chiama - Inizializzazione del valore, la seconda - Inizializzazione predefinita. Ci sono sottigliezze e differenze a seconda del tipo di oggetto (se è un aggregato o ha un costruttore predefinito, ...). Per esempio, il risultato qui sarà assolutamente diverso quando si usano le parentesi e senza di esse:

#include <iostream>
using namespace std;
struct S {
        int i;
        int q;
        int f;
};
struct Q : S {
        Q():S() {}
        //Q() {}
};
int main() {
        Q s;
        cout << s.i << s.q << s.f << endl;
}

Se volete afferrare la teoria fino alle vostre orecchie, andate qui https://en.cppreference.com/w/cpp/language/initialization

ZS: non è tutto rilevante per il mcl, ma come comprensione del modello di riferimento.

Initialization - cppreference.com
  • en.cppreference.com
Initialization of a variable provides its initial value at the time of construction. The initial value may be provided in the initializer section of a declarator or a new expression. It also takes place during function calls: function parameters and the function return values are also initialized. For each declarator, the initializer may be one...
 
Vict:

Se volete entrare nella teoria fino alle orecchie, andate qui https://en.cppreference.com/w/cpp/language/initialization

Non è ancora necessario, ma lo salverò per sapere dove leggere la fonte.

Grazie a tutti!

Vladimir Simakov:
Nei plus, la seconda opzione è considerata una cattiva forma.

Ci sarà lo stesso problema che ho incontrato io - duplicazione del codice per l'inizializzazione dei campi, quando diventa necessario scrivere più di un costruttore nella classe

se usate la variante 2, potete mettere il codice duplicato in un metodo separato e chiamare questo metodo dopo aver eseguito le azioni necessarie in ogni costruttore (ho l'inizializzazione della classe per struttura e la variante 2 per il nome del file dove sono scritti i dati di questa struttura (salvataggio di backup))


Scrivere che ripetere il codice è male... Non è interessante, ma il problema è che se aggiungo un nuovo campo alla classe, devo ricordarmi di inizializzare questo campo tante volte quanti sono i costruttori - il fatto che non sia conveniente, ma il fatto che si possa dimenticare di farlo N volte, è un problema, imho

 
Igor Makanu:

Non è ancora necessario, ma lo terrò, saprò dove leggere la fonte originale.

Grazie a tutti!

Ci sarà lo stesso problema che ho incontrato io - duplicazione del codice per l'inizializzazione dei campi, quando diventa necessario scrivere più di un costruttore nella classe

se usate la variante 2, potete mettere il codice duplicato in un metodo separato e chiamare questo metodo dopo le azioni necessarie in ogni costruttore (ho l'inizializzazione della classe per struttura e la variante 2 per il nome del file dove sono scritti i dati di questa struttura (salvataggio di backup))


Scrivere che ripetere il codice è male... Non è interessante, ma il problema è che se aggiungo un nuovo campo alla classe, devo ricordarmi di inizializzare questo campo tante volte quanti sono i costruttori - il fatto che non sia conveniente, ma il fatto che si possa dimenticare di farlo N volte, è un problema, imho

Una macro che può anche essere parametrica. Dimenticanza (ne soffro io stesso), tratta anche con questo))))

 
Igor Makanu:

ci sarà lo stesso problema che ho incontrato io - duplicazione del codice di inizializzazione dei campi quando c'è bisogno di scrivere più di un costruttore nella classe

Se usate la variante 2, potete mettere il codice duplicato in un metodo separato e chiamare questo metodo dopo le azioni necessarie in ogni costruttore (ho l'inizializzazione della classe per struttura e la variante 2 per il nome di un file dove i dati di questa struttura sono scritti (salvataggio di backup))


Scrivere che ripetere il codice è male... Non è interessante, ma il problema è che se aggiungo un nuovo campo alla classe, dovrò ricordarmi di inizializzare questo campo tante volte quanti sono i costruttori - il fatto che non sia conveniente, ma il fatto che si possa dimenticare di farlo N volte, è un problema, imho

Andiamo, tutto può essere risolto:

class Q {
    void ctr_base(T t, S s) {//make init tasks
        }
public:
    Q(T t, S s, int i) {
        // make base init
        ctr_base(t, s);
        // make additional tasks
        ...
    }
    Q(T t, S s, strint str) {
        // make base init
        ctr_base(t, s);
        // make additional tasks
        ...
    }
};

Non sono contro i costruttori di default (o l'inizializzazione di default), ma se il tuo costruttore lascia l'oggetto (beh, se non è un aggregato stupido) in uno stato indefinito e poi lo inizializzi tramite qualche stampella, allora stai facendo tutto sbagliato.

ZS: a proposito, è possibile delegare il costruttore nei professionisti, mancando ovviamente

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int)
};
 
Vict:

Dai, è risolvibile:

un po' più avanti di me, si è appena seduto al PC.

Il consiglio di@Vladimir Simakov di inizializzare i campi della classe usando una macro, per non dimenticare di inizializzare correttamente, beh, sì - buon trucco, ma il codice verrà letto come una chiamata a un metodo che inizializza i campi, difficile supporre che questo sia un buon tono...


Anche il tuo esempio non è il più raffinato, ma risolve il problema esattamente nel modo in cui l'ho fatto ora: un metodo di inizializzazione separato.

imho, è una questione di scopo - il modo giusto per scrivere una classe base con tutti i campi necessari, ereditare da essa, rendere il costruttore protetto e inizializzare la classe base dagli eredi e così sarà la protezione da "dimenticare" - non c'è un costruttore predefinito? - quindi almeno potete guardare su mikrosoft se cercate su google "protected constructor" - ci sarà un articolo


Il mio problema è un po' diverso, ho lasciato volutamente l'ereditarietà, per salvare lo stato dei campi della classe in un file, ho 2 campi di 2 classi, e il loro stato è anche salvato nello stesso file chiamando i metodi appropriati. Ho provato a salvare tutto quando ho ereditato da una classe base, è diventato molto disordinato, l'ho riscritto senza ereditarietà, tutto è diventato "trasparente" ora.

 
Igor Makanu:

Un po' più avanti di me, si è appena seduto al PC.

Il consiglio di@Vladimir Simakov di inizializzare i campi della classe usando una macro, per non dimenticare di inizializzare correttamente, beh, sì - buon trucco, ma il codice si leggerà come una chiamata a un metodo che inizializza i campi, difficile supporre quanto sia buono il tono...


Anche il tuo esempio non è il più raffinato, ma risolve il problema esattamente nel modo in cui l'ho fatto ora: un metodo di inizializzazione separato.

imho, è una questione di scopo - il modo giusto per scrivere una classe base con tutti i campi necessari, ereditare da essa, rendere il costruttore protetto e inizializzare la classe base dagli eredi e così sarà la protezione dal "dimenticare" - non c'è un costruttore di default, giusto? - quindi almeno potete guardare su mikrosoft se cercate su google "protected constructor" - ci sarà un articolo


Il mio problema è un po' diverso, ho lasciato volutamente l'ereditarietà, per salvare lo stato dei campi della classe in un file, ho 2 campi di 2 classi, e il loro stato è anche salvato nello stesso file chiamando i metodi appropriati. Ho provato a salvare tutto quando ho ereditato da una classe base, è diventato molto complicato; l' ho riscritto senza ereditarietà, tutto è diventato "trasparente" ora.

Cosa c'è di così confuso?

...
virtual void CBase::Save(int hndl){...}
...
CObj:public CBase
...
void CObj::Save(int hndl){
   CBase::Save(hndl);
   ...
}
 
Vladimir Simakov:

Cosa c'è di così confuso?

Questo è esattamente quello da cui mi sono allontanato, e all'inizio ho fatto

con questo approccio - ereditare da una classe base, "dove tutto è" - tutto funziona, ma fino a quando non vogliamo provare a fare diversi campi di classe, e poi vogliamo aggiungere diversi campi in ogni classe campo e inchiodare il tutto con un array dinamico di classi

e poi otterremo che non possiamo implementare il metodo Save(int hndl) stesso in una classe base - questo metodo sarà in realtà un'interfaccia, che, come discusso sopra, non sarà affatto necessaria - si può scrivere senza interfacce

Di solito massimizzo i miei sforzi sulla flessibilità del codice - minimo di confusione - il risultato è un nuovo problema risolto, che la mia terminologia sia perdonata)))


il problema sarà, quando si implementa il salvataggio su file, è necessario elaborare l'intestazione del file, che dovrebbe descrivere la quantità totale di diversi tipi di voci e poi è necessario non perdere la logica / ordine di salvataggio dei dati, al fine di leggere tutto correttamente.... e il finale sarà se si cambia anche un solo campo in una classe

imho, la laboriosità di tale eredità "facile" da una classe base - si riduce allo sviluppo e alla manutenzione di un piccolo database, al monitoraggio costante dei cambiamenti in qualsiasi classe