MQL5 Il compilatore non distingue tra una classe e un puntatore ad essa - pagina 13

 
Ilya Malev:

Solo che non è lo stesso in MCL come in C++. Qui un puntatore non è affatto un tipo di dati separato, quindi entrambi sono oggetti, e una variabile contiene il suo handle.

giusto?

// тот же класс А что был выше

A a; // будет создан автообъект с реальным выделением памяти и инициализацией. 'a' является указателем со статусом POINTER_AUTOMATIC

A* pA; // память под объект не выделена, объекта нет. pA является указателем со статусом POINTER_INVALID или POINTER_DYNAMIC?

Ma non posso dire con certezza quale stato ottiene il puntatore dopo la creazione:POINTER_INVALID o POINTER_DYNAMIC. In teoria, dovrebbe essere POINTER_INVALID finché non ottiene l'indirizzo da new.

 
Ilya Malev:

Cioè dichiarare

E avrete esattamente lo stesso "auto-oggetto", ma dovrete cancellarlo voi stessi.

Non è un'auto...

 
SemenTalonov:

Non posso dire quale stato ottiene il puntatore dopo la creazione,POINTER_INVALID o POINTER_DYNAMIC. Nell'idea, dovrebbe essere POINTER_INVALID finché il nuovo non ottiene l'indirizzo.

È corretto, lo stato sarà POINTER_INVALID. Ma le variabili a e pA sono dello stesso tipo. Solo a è una costante, il costruttore è chiamato alla creazione e il distruttore all'uscita dal blocco, mentre pA è accessibile in modo casuale e il costruttore e il distruttore sono chiamati arbitrariamente.

Una variabile dichiarata senza * non può ottenere lo stato POINTER_INVALID senza trucchi speciali, questo è anche vero, ma non perché le variabili sono di tipo diverso, ma perché il compilatore controlla bene le chiamate al costruttore e al distruttore, e vieta di assegnare un altro valore.

E poiché le variabili sono essenzialmente dello stesso tipo, non c'è una logica per accedere ai metodi di classe attraverso di esse in modo diverso

 
Ilya Malev:

Questo è corretto, lo stato sarà POINTER_INVALID. Ma le variabili a e pA sono dello stesso tipo. Solo a è costante e il costruttore è chiamato alla creazione, e il distruttore all'uscita dal blocco, e pA è con accesso casuale, e con chiamate casuali al costruttore e al distruttore.

Quindi tutti i problemi derivano dal fatto che bisogna distinguerli. Cioè un puntatore richiede un atteggiamento più responsabile (quelloDINAMICO).

 
SemenTalonov:

L'unica ragione del problema è che devono essere distinti. Cioè un puntatore richiede un atteggiamento più responsabile (quelloDINAMICO).

Secondo me, il punto dell'OOP è solo che i riferimenti agli oggetti possono essere passati e memorizzati, e si possono scrivere classi diverse con comportamenti diversi. Iniziando a memorizzare un riferimento a un "auto-oggetto" senza nemmeno toccare il polimorfismo, si perde già tutta la loro distinzione (poiché non ci sono variabili utente di tipo A&)

 
Ilya Malev:

Una variabile dichiarata senza * non può ricevere lo stato POINTER_INVALID senza trucchi speciali, anche questo è vero, ma non perché le variabili siano di tipo diverso, ma perché il compilatore controlla bene le chiamate al costruttore e al distruttore, e vieta di assegnarle un valore diverso.

Esattamente. Il puntatore auto-oggetto non cambierà il suo stato, mentre il puntatorePOINTER_DYNAMIC potrebbe diventare non valido in qualsiasi momento dell'esecuzione del programma. Le ragioni non sono così importanti quanto la possibilità di un tale evento in sé.

 
SemenTalonov:

Esattamente. Il puntatore auto-object non cambierà il suo stato, mentre il puntatorePOINTER_DYNAMIC potrebbe diventare non valido mentre il programma è in esecuzione. Le ragioni non sono così importanti quanto la possibilità di un tale evento in sé.

C'è una cura eccellente e sicura per questo:

class A{~A(){}};

void OnStart()
 {
  A*a=new A;
  delete a; // oops =))
 };
Beh, penso che i programmatori dovrebbero guardare il tempo di vita degli oggetti e i modi di accesso alle variabili stesse, mentre un'architettura ben progettata minimizzerà gli errori un anno o due dopo aver scritto il codice...
 
Infatti, la durata dell'oggetto deve corrispondere alla durata dei puntatori "live" ad esso. Naturalmente, l'oggetto dinamico POINTER_DYNAMIC è anche una soluzione a metà strada, che crea problemi per una codifica sconsiderata. Ma POINTER_AUTOMATIC non mi dà nemmeno quella scalabilità di cui ho bisogno per gestire correttamente gli oggetti. Abbiamo bisogno che sia così - se nessun puntatore all'oggetto è stato creato quando si esce dal blocco, tranne l'autovariabile in cui è stato creato, allora cancella l'oggetto. Se i riferimenti sono stati ricevuti al di fuori del blocco corrente - non cancellare l'oggetto mentre questi riferimenti esistono ancora. Allora ci sarà scalabilità e il codificatore non dovrà guardare la cancellazione da solo tutto il tempo... (per esempio, se ora scrivete A* a = new A; e poi a = new A di nuovo, il primo oggetto sarà perso per sempre ed è garantito che ci saranno errori di perdita di memoria nei log all'uscita del programma. E dove guarda il famoso ottimizzatore di codice)?
 
Ilya Malev:
Essenzialmente, la durata di un oggetto deve corrispondere alla durata dei puntatori "live" ad esso. Naturalmente, l'oggetto dinamico di tipo mkl POINTER_DYNAMIC è anche una soluzione semi-opzionale che causa problemi quando la codifica non è molto efficiente. Ma POINTER_AUTOMATIC non mi dà nemmeno quella scalabilità di cui ho bisogno per gestire correttamente gli oggetti. Abbiamo bisogno che sia così - se nessun puntatore all'oggetto è stato creato quando si esce dal blocco, tranne l'autovariabile dove è stato creato, allora cancella l'oggetto. Se i riferimenti sono stati ricevuti al di fuori del blocco corrente - non cancellare l'oggetto mentre questi riferimenti sono ancora vivi. Allora ci sarà scalabilità e il codificatore non dovrà guardare la cancellazione da solo tutto il tempo... (per esempio, se ora scrivete A* a = new A; e poi a = new A di nuovo, il primo oggetto sarà perso per sempre ed è garantito che ci saranno errori di perdita di memoria nei log all'uscita del programma. E dove guarda l'ottimizzatore di codice celebrato)?

Anche questa è stata una bella sorpresa. Dopo tutto, sa esattamente ogni byte che è scappato, ma non vuole rilasciarlo. Quindi al momento non guarda affatto i dinosauri? Conta semplicemente la differenza tra la memoria richiesta e quella liberata alla fine.

E la durata della vita e i puntatori vuoti/perduti sono solo uno dei problemi. Prima abbiamo discusso i problemi relativi alla conversione implicita di puntatori a oggetti, è qui che è un incidente) Quando per errore (ad esempio il compilatore non deve aver permesso di compilare tale codice), invece del puntatore previsto si può ottenere una copia di un oggetto tramite puntatore, che a sua volta può puntare a nessun luogo). Beh, lei stesso ha già scritto delle operazioni di confronto. Tutto lì è anche molto implicito.

 
SemenTalonov:

1. Anche questa è stata una bella sorpresa. Dopo tutto, conosce esattamente ogni byte che è scappato e non vuole liberarlo. Quindi al momento non guarda affatto i dinosauri? Conta solo la differenza tra la memoria richiesta e quella rilasciata alla fine.

E la durata della vita e i puntatori vuoti/perduti sono solo uno dei problemi.

1. Non proprio, scrivere un semplice GC per gli oggetti dinamici sarebbe un gioco da ragazzi per gli sviluppatori, ma hanno lasciato questi messaggi di proposito, in modo che i programmatori possano vedere che hanno un difetto nel loro programma. Perché i loro oggetti dinamici sono un semi-C# (non sono un esperto in materia, ma da quello che ho sentito) - come se gli oggetti si comportassero allo stesso modo (niente puntatori, ma tutto è oggetti), e nessun sottosistema elaborato per loro è stato sviluppato.

2. Beh, sì, certo, se le variabili oggetto fossero dello stesso tipo (cioè: cancellate non automaticamente o indipendentemente, ma dal raccoglitore di rifiuti integrato quando non ci sono riferimenti a loro), allora tutti i riferimenti a loro sarebbero esattamente gli stessi.

Motivazione: