Strutture, Classi e Interfacce
Strutture
Una struttura è un insieme di elementi di qualsiasi tipo (ad eccezione del tipo void). Così, la struttura combina dati logicamente correlati di tipo diverso.
Dichiarazione Struttura
Il tipo di dati struttura è determinato dalla seguente descrizione:
struct structure_name
{
elements_description
};
|
Il nome struttura non può essere usato come un identificatore (nome di una variabile o funzione). Va notato che in MQL5 elementi strutturali si susseguono direttamente, senza allineamento. In C++ un tale ordine è fatto per il compilatore utilizzando la seguente istruzione:
Se si vuole fare un altro allineamento nella struttura, utilizzare i membri ausiliari, "fillers" per la giusta dimensione.
Esempio:
struct trade_settings
{
uchar slippage; // valore grandezza-slippage permissibile 1 byte
char reserved1; // salta 1 byte
short reserved2; // salta 2 bytes
int reserved4; // altri 4 bytes vengono saltati. garantisce l'allineamento del bordo 8 byte
double take; // valore del prezzo del fissaggio del profitto
double stop; // valore del prezzo dello stop protettivo
};
|
Tale descrizione delle strutture allineate è necessaria solo per il trasferimento di funzioni importate dll.
Attenzione: Questo esempio illustra dati non correttamente designati. Sarebbe meglio prima dichiarare take e stop dati di grandi dimensioni del tipo double, e quindi dichiarare lo slippagemembro del tipo uchar. In questo caso, la rappresentazione interna dei dati sarà sempre la stessa indipendentemente dal valore specificato in #pragma pack().
Se una struttura contiene variabili di tipo string e/o oggetto di un array dinamico, il compilatore assegna un costruttore implicito a tale struttura. Questo costruttore resetta tutti i membri della struttura di tipo string ed inizializza correttamente oggetti della matrice dinamica.
Strutture Semplici
Le strutture che non contengono stringhe, oggetti delle classi, puntatori ed oggetti di array dinamici sono chiamate Strutture Semplici. Le variabili di strutture semplici e le loro array possono essere passate come parametri alle funzioni importate da DLL.
La copia di strutture semplici è consentita solo in due casi:
- Se gli oggetti appartengono allo stesso tipo di struttura
- Se gli oggetti sono collegati dalla linea che significa che una struttura è un discendente di un'altra.
Per fornire un esempio, sviluppiamo la struttura personalizzata CustomMqlTick con il suo contenuto identico a quella built-in MqlTick. Il compilatore non consente di copiare il valore dell'oggetto MqlTick nell'oggetto di tipo CustomMqlTick. Typecasting diretto al tipo necessario provoca anche l'errore di compilazione:
//--- la copia di strutture semplici di diversi tipi è vietata
my_tick1=last_tick; // il compilatore restituisce un errore qui
//--- fare il typecasting di strutture di tipi differenti l'un l'altra è vietato anche
my_tick1=(CustomMqlTick)last_tick;// il compilatore restituisce un errore qui
|
Pertanto, resta una sola opzione – la copia dei valori degli elementi della struttura uno ad uno. È comunque permesso copiare i valori dello stesso tipo di CustomMqlTick.
CustomMqlTick my_tick1,my_tick2;
//--- è consentito copiare gli oggetti dello stesso tipo di CustomMqlTick nel seguente modo
my_tick2=my_tick1;
//--- creare un array dagli oggetti della struttura semplice CustomMqlTick e scrivere valori in esso
CustomMqlTick arr[2];
arr[0]=my_tick1;
arr[1]=my_tick2;
|
La funzione ArrayPrint() è chiamata per un controllo per visualizzare la funzione arr[] valore array nel journal.
//+------------------------------------------------------------------+
//| Funzione start programma Script |
//+------------------------------------------------------------------+
void OnStart()
{
// --- sviluppa la struttura simile al built-in MqlTick
struct CustomMqlTick
{
datetime time; // Ora di aggiornamento del prezzo Last
double bid; // Prezzo Bid corrente
double ask; // Prezzo Ask corrente
double last; // Prezzo corrente dell'ultimo trade (Last)
ulong volume; // Volume per il corrente prezzo Last
long time_msc; // Tempo di aggiornamento del prezzo Last in millisecondi
uint flags; // Tick flags
};
//--- ottiene l'ultimo valore tick
MqlTick last_tick;
CustomMqlTick my_tick1,my_tick2;
//--- tentativo di copiare dati da MqlTick a CustomMqlTick
if(SymbolInfoTick(Symbol(),last_tick))
{
//--- la copia di strutture semplici non correlate è vietata
//1. my_tick1=last_tick; // il compilatore restituisce un errore qui
//--- il typecastin di strutture non correlate l'una all'altra, è vietato, pure
//2. my_tick1=(CustomMqlTick)last_tick;// il compilatore restituisce un errore qui
//--- quindi copia i membri della struttura uno per uno
my_tick1.time=last_tick.time;
my_tick1.bid=last_tick.bid;
my_tick1.ask=last_tick.ask;
my_tick1.volume=last_tick.volume;
my_tick1.time_msc=last_tick.time_msc;
my_tick1.flags=last_tick.flags;
//--- è consentito copiare gli oggetti dello stesso tipo di CustomMqlTick nel seguente modo
my_tick2=my_tick1;
//--- creare un array dagli oggetti della struttura semplice CustomMqlTick e scrivere valori in esso
CustomMqlTick arr[2];
arr[0]=my_tick1;
arr[1]=my_tick2;
ArrayPrint(arr);
/ --- esempio di visualizzazione dei valori dell'array contenente gli oggetti del tipo CustomMqlTick
/*
[time] [bid] [ask] [last] [volume] [time_msc] [flags]
[0] 2017.05.29 15:04:37 1.11854 1.11863 +0.00000 1450000 1496070277157 2
[1] 2017.05.29 15:04:37 1.11854 1.11863 +0.00000 1450000 1496070277157 2
*/
}
else
Print("SymbolInfoTick() failed, error = ",GetLastError());
}
|
Il secondo esempio mostra le caratteristiche della copia di strutture semplici dalla discendenza. Supponiamo di avere la struttura base Animal, da cui derivano le strutture Cat e Dog. Possiamo copiare gli oggetti Animal e Cat, così come gli oggetti Animal e Dog, ma non possiamo copiare Cat e Dog a vicenda, anche se entrambi sono discendenti della struttura Animal.
//--- struttura per descrivere i cani
struct Dog: Animal
{
bool hunting; // razza da caccia
};
//--- struttura per descrivere i gatti
struct Cat: Animal
{
bool home; // animale domestico
};
//--- creare oggetti di strutture figlio
Dog dog;
Cat cat;
//--- può essere copiato da antenato a discendente (Animal ==> Dog)
dog=some_animal;
dog.swim=true; // i cani possono nuotare
//--- non è possibile copiare oggetti di strutture figlie (Dog != Cat)
cat=dog; // il compilatore restituisce un errore
|
Codice completo di esempio:
//--- struttura di base per la descrizione degli animali
struct Animal
{
int head; // numero di teste
int legs; // numero di zampe
int wings; // numero di ali
bool tail; // coda
bool fly; // che vola
bool swim; // che nuota
bool run; // che corre
};
//--- struttura per descrivere i cani
struct Dog: Animal
{
bool hunting; // razza da caccia
};
//--- struttura per descrivere i gatti
struct Cat: Animal
{
bool home; // animale domestico
};
//+------------------------------------------------------------------+
//| Funzione start programma Script |
//+------------------------------------------------------------------+
void OnStart()
{
//--- crea e descrive un oggetto del tipo di base Animal
Animal some_animal;
some_animal.head=1;
some_animal.legs=4;
some_animal.wings=0;
some_animal.tail=true;
some_animal.fly=false;
some_animal.swim=false;
some_animal.run=true;
//--- crea oggetti di tipo figlio
Dog dog;
Cat cat;
//--- può essere copiato da antenato a discendente (Animal ==> Dog)
dog=some_animal;
dog.swim=true; // i cani possono nuotare
//--- non è possibile copiare oggetti di strutture figlie (Dog != Cat)
//cat=dog; // il compilatore restituisce errore qui
//--- è pertanto possibile copiare gli elementi uno per uno, solamente
cat.head=dog.head;
cat.legs=dog.legs;
cat.wings=dog.wings;
cat.tail=dog.tail;
cat.fly=dog.fly;
cat.swim=false; // cats cannot swim
//--- è possibile copiare i valori dal discendente all'antenato
Animal elephant;
elephant=cat;
elephant.run=false;// gli elefanti non possono correre
elephant.swim=true;// gli elefanti possono nuotare
//--- crea un array
Animal animals[4];
animals[0]=some_animal;
animals[1]=dog;
animals[2]=cat;
animals[3]=elephant;
//--- stampare
ArrayPrint(animals);
//--- risultato dell' esecuzione
/*
[head] [legs] [wings] [tail] [fly] [swim] [run]
[0] 1 4 0 true false false true
[1] 1 4 0 true false true true
[2] 1 4 0 true false false false
[3] 1 4 0 true false true false
*/
}
|
Un altro modo per copiare i tipi semplici è l'utilizzo dell' unione. Gli oggetti delle strutture dovrebbero essere membri della stessa unione - vedere l'esempio in union.
L'accesso ai Membri Struttura
La struttura è un nuovo tipo di dati che consente di dichiarare variabili di questo tipo. La struttura può essere dichiarata solo una volta all'interno di un progetto. I membri della struttura sono accessibili usando l'operazione point(.).
Esempio:
struct trade_settings
{
double take; // valori della fissazione dei prezzi profitto
double stop; // valore del prezzo di stop protettivo
uchar slippage; // valore dello scostamento accettabile
};
//--- creare ed inizializza una variabile di tipo trade_settings
trade_settings my_set={0.0,0.0,5};
if (input_TP>0) my_set.take=input_TP;
|
'pack' per allineare i campi struttura e classe #
Lo speciale l'attributo pacck consente di impostare l'allineamento dei campi della struttura o della classe.
dove n è uno dei seguenti valori: 1, 2, 4, 8 o 16. Potrebbe essere assente.
Esempio:
struct pack(sizeof(long)) MyStruct
{
// i membri della struttura devono essere allineati al limite di 8 byte
};
or
struct MyStruct pack(sizeof(long))
{
// i membri della struttura devono essere allineati al limite di 8 byte
};
|
'pack (1)' è applicato di default per le strutture. Ciò significa che i membri della struttura si trovano uno dopo l'altro in memoria e la dimensione della struttura è uguale alla somma della dimensione dei membri.
Esempio:
//+------------------------------------------------------------------+
// | Funzione Start del programma di Script |
//+------------------------------------------------------------------+
void OnStart()
{
//--- struttura semplice senza allineamento
struct Simple_Structure
{
char c; // sizeof(char)=1
short s; // sizeof(short)=2
int i; // sizeof(int)=4
double d; // sizeof(double)=8
};
//--- dichiara una semplice istanza di struttura
Simple_Structure s;
//--- mostra la dimensione di ogni membro della struttura
Print("sizeof(s.c)=",sizeof(s.c));
Print("sizeof(s.s)=",sizeof(s.s));
Print("sizeof(s.i)=",sizeof(s.i));
Print("sizeof(s.d)=",sizeof(s.d));
//--- assicurati che la dimensione della struttura POD sia uguale alla somma della dimensione dei suoi membri
Print("sizeof(simple_structure)=",sizeof(simple_structure));
/*
Result:
sizeof(s.c)=1
sizeof(s.s)=2
sizeof(s.i)=4
sizeof(s.d)=8
sizeof(simple_structure)=15
*/
}
|
L'allineamento dei campi della struttura può essere necessario quando si scambiano dati con librerie di terze parti (*.DLL) in cui viene applicato tale allineamento.
Usiamo alcuni esempi per mostrare come funziona l'allineamento. Applicheremo una struttura composta da quattro membri senza allineamento.
//--- struttura semplice senza allineamento
struct Simple_Structure pack() // nessuna dimensione è specificata, l'allineamento al limite di 1 byte deve essere impostato
{
char c; // sizeof(char)=1
short s; // sizeof(short)=2
int i; // sizeof(int)=4
double d; // sizeof(double)=8
};
//--- dichiara una semplice istanza di struttura
Simple_Structure s;
|
I campi della struttura devono essere collocati in memoria uno dopo l'altro in base all'ordine di dichiarazione e dimensione del tipo. La dimensione della struttura è 15, mentre un offset rispetto ai campi della struttura negli array non è definito.

Ora dichiariamo la stessa struttura con l'allineamento di 4 byte ed eseguire il codice.
//+------------------------------------------------------------------+
// | Funzione Start del programma di Script |
//+------------------------------------------------------------------+
void OnStart()
{
//--- struttura semplice con l'allineamento a 4 byte
struct Simple_Structure pack(4)
{
char c; // sizeof(char)=1
short s; // sizeof(short)=2
int i; // sizeof(int)=4
double d; // sizeof(double)=8
};
//--- dichiara una semplice istanza di struttura
Simple_Structure s;
//--- mostra la dimensione di ogni membro della struttura
Print("sizeof(s.c)=",sizeof(s.c));
Print("sizeof(s.s)=",sizeof(s.s));
Print("sizeof(s.i)=",sizeof(s.i));
Print("sizeof(s.d)=",sizeof(s.d));
//--- assicurati che la dimensione della struttura POD non sia ora uguale alla somma della dimensione dei suoi membri
Print("sizeof(simple_structure)=",sizeof(simple_structure));
/*
Result:
sizeof(s.c)=1
sizeof(s.s)=2
sizeof(s.i)=4
sizeof(s.d)=8
sizeof(simple_structure)=16 // la dimensione della struttura è cambiata
*/
}
|
La dimensione della struttura è cambiata in modo che tutti i membri di 4 byte e più abbiano un offset dall'inizio della struttura multipla di 4 byte. I membri più piccoli devono essere allineati al loro limite di dimensione (ad esempio, 2 per "short"). Ecco come appare (il byte aggiunto è mostrato in grigio).

In questo caso, 1 byte viene aggiunto dopo il sc membro, in modo che il campo s.s (sizeof (short)==2) ha il limite di 2 byte (allineamento per il tipo 'short').
Anche l'offset all'inizio della struttura nell'array è allineato al limite di 4 byte, ovvero gli indirizzi degli elementi a [0], a[1] ed a[n] devono essere multipli di 4 byte per Simple_Structure arr[].
Consideriamo altre due strutture costituite da tipi simili con un allineamento di 4 byte ma un ordine di membro diverso. Nella prima struttura, i membri si trovano nell'ordine crescente della dimensione del tipo.
//+------------------------------------------------------------------+
// | Funzione Start del programma di Script |
//+------------------------------------------------------------------+
void OnStart()
{
//--- semplice struttura allineata al limite di 4 byte
struct CharShortInt pack(4)
{
char c; // sizeof(char)=1
short s; // sizeof(short)=2
int i; // sizeof(double)=4
};
//--- dichiara una semplice istanza di struttura
CharShortInt ch_sh_in;
//--- mostra la dimensione di ogni membro della struttura
Print("sizeof(ch_sh_in.c)=",sizeof(ch_sh_in.c));
Print("sizeof(ch_sh_in.s)=",sizeof(ch_sh_in.s));
Print("sizeof(ch_sh_in.i)=",sizeof(ch_sh_in.i));
//--- assicurati che la dimensione della struttura POD sia uguale alla somma della dimensione dei suoi membri
Print("sizeof(CharShortInt)=",sizeof(CharShortInt));
/*
Result:
sizeof(ch_sh_in.c)=1
sizeof(ch_sh_in.s)=2
sizeof(ch_sh_in.i)=4
sizeof(CharShortInt)=8
*/
}
|
Come possiamo vedere, la dimensione della struttura è 8 e consiste dei due blocchi da 4 byte. Il primo blocco contiene i campi con i tipi "char" e "short", mentre il secondo contiene il campo con il tipo 'int'.

Ora trasformiamo la prima struttura nella seconda, che differisce solo nell'ordine dei campi, spostando il membro di tipo 'short' alla fine.
//+------------------------------------------------------------------+
// | Funzione Start del programma di Script |
//+------------------------------------------------------------------+
void OnStart()
{
//--- semplice struttura allineata al limite di 4 byte
struct CharIntShort pack(4)
{
char c; // sizeof(char)=1
int i; // sizeof(double)=4
short s; // sizeof(short)=2
};
//--- dichiara una semplice istanza di struttura
CharIntShort ch_in_sh;
//--- mostra la dimensione di ogni membro della struttura
Print("sizeof(ch_in_sh.c)=",sizeof(ch_in_sh.c));
Print("sizeof(ch_in_sh.i)=",sizeof(ch_in_sh.i));
Print("sizeof(ch_in_sh.s)=",sizeof(ch_in_sh.s));
//--- assicurati che la dimensione della struttura POD sia uguale alla somma della dimensione dei suoi membri
Print("sizeof(CharIntShort)=",sizeof(CharIntShort));
/*
Result:
sizeof(ch_in_sh.c)=1
sizeof(ch_in_sh.i)=4
sizeof(ch_in_sh.s)=2
sizeof(CharIntShort)=12
*/
}
|
Sebbene il contenuto della struttura non sia cambiato, l'alterazione della sequenza dei membri ha aumentato le sue dimensioni.

L'allineamento dovrebbe anche essere considerato quando si eredita. Dimostriamolo usando la semplice struttura Parent con un singolo membro di tipo 'char'. La dimensione della struttura senza allineamento è 1.
struct Parent
{
char c; // sizeof(char)=1
};
|
Creiamo la classe figlia Children con il membro di tipo "short" (sizeof (short)=2).
struct Children pack(2) : Parent
{
short s; // sizeof(short)=2
};
|
Di conseguenza, quando si imposta l'allineamento su 2 byte, la dimensione della struttura è uguale a 4, sebbene la dimensione dei suoi membri sia 3. In questo esempio, 2 byte devono essere allocati alla classe Parent, in modo che l'accesso al campo "short" della classe figlia sia allineato a 2 byte.
La conoscenza di come viene allocata la memoria per i membri della struttura è necessaria se un'applicazione MQL5 interagisce con i dati di terze parti scrivendo/leggendo sul livello dei file o degli stream.
La directory MQL5\Include\WinAPI della Libreria standard contiene le funzioni per lavorare con le funzioni WinAPI. Queste funzioni applicano le strutture con un allineamento specificato per i casi quando è richiesto per lavorare con WinAPI.
offsetof è un comando speciale direttamente correlato all'attributo pack. Ci consente di ottenere un offset membro dall'inizio della struttura.
//--- dichiara la variabile di tipo Children
Children child;
//--- rileva gli offset dall'inizio della struttura
Print("offsetof(Children,c)=",offsetof(Children,c));
Print("offsetof(Children,s)=",offsetof(Children,s));
/*
Result:
offsetof(Children,c)=0
offsetof(Children,s)=2
*/
|
Modificatore 'final' #
L'uso del modificatore 'final' durante la dichiarazione struttura vieta ulteriori eredità da questa struttura. Se una struttura non necessita di ulteriori modifiche, o le modifiche non sono ammesse per motivi di sicurezza, dichiarare tale struttura con il modificatore 'final'. Inoltre, tutti i componenti della struttura saranno implicitamente considerati definitivi.
struct settings final
{
//--- Corpo della struttura
};
struct trade_settings : public settings
{
//--- Corpo della struttura
};
|
Se si tenta di ereditare da una struttura con il modificatore 'final', come mostrato nell'esempio precedente, il compilatore restituirà un errore:
non può ereditare da 'Impostazioni', siccome è stato dichiarato come 'final'
vedi dichiarazione di 'impostazioni'
|
Classi #
Le classi differiscono dalle strutture in base a quanto riportato qui di seguito:
- la parola chiave class viene utilizzata nella dichiarazione;
- Per impostazione predefinita, tutti i membri della classe hanno lo specificatore di accesso private, se non diversamente indicato. Data-membri della struttura hanno il tipo predefinito di accesso come public, salvo diversa indicazione;
- oggetti della classe hanno sempre una tabella di funzioni virtuali, anche se non ci sono funzioni virtuali dichiarate nella classe. Le strutture non possono avere funzioni virtuali;
- l'operatore new può essere applicato ad oggetti della classe, questo operatore non può essere applicato a strutture;
- le classi possono essere ereditate solo dalle classi; le strutture possono essere ereditate solo dalle strutture.
Le classi e le strutture possono avere un costruttore e distruttore espliciti. Se il costruttore è definito in modo esplicito, l'inizializzazione di una struttura o variabile della classe, utilizzando la sequenza di inizializzazione, è impossibile.
Esempio:
struct trade_settings
{
double take; // valori della fissazione dei prezzi profitto
double stop; // valore del prezzo di stop protettivo
uchar slippage; // valore dello scostamento accettabile
//--- Costruttore
trade_settings() { take=0.0; stop=0.0; slippage=5; }
//--- Distruttore
~trade_settings() { Print("Questa è la fine"); }
};
//--- Il compilatore genera un messaggio di errore riferendo che l'inizializzazione è impossibile
trade_settings my_set={0.0,0.0,5};
|
Costruttori e distruttori
Un costruttore è una funzione speciale, che viene chiamata automaticamente quando si crea un oggetto di una struttura o di una classe, e di solito è usato per inizializzare membri della classe. Inoltre parleremo solo di classi, mentre lo stesso vale per le strutture, se non diversamente indicato. Il nome di un costruttore deve corrispondere al nome della classe. Il costruttore non ha alcun tipo di ritorno (è possibile specificare il tipo void).
I membri definiti della classe - stringhe, array dinamici ed oggetti che richiedono inizializzazioni - verranno in ogni caso inizializzati, indipendentemente dal fatto che vi sia un costruttore.
Ogni classe può avere multipli costruttori, differendo per il numero di parametri e la lista di inizializzazione. Un costruttore che richiede il fatto di specificare i parametri, viene chiamato un costruttore parametrico.
Un costruttore senza parametri viene chiamato un costruttore di default. Se non vi sono costruttori dichiarati in una classe, il compilatore crea un costruttore di default durante la compilazione.
//+--------------------------------------------------------------------------------+
//| La classe per lavorare con una data |
//+--------------------------------------------------------------------------------+
class MyDateClass
{
private:
int m_year; // Anno
int m_month; // Mese
int m_day; // Giorno del mese
int m_hour; // Ore in un giorno
int m_minute; // Minuti
int m_second; // Secondi
public:
//--- Costruttore default
MyDateClass(void);
//--- Costruttore parametrico
MyDateClass(int h,int m,int s);
};
|
Un costruttore può essere dichiarato nella descrizione della classe e poi il suo corpo può essere definito. Ad esempio, due costruttori di MyDateClass possono essere definiti nel modo seguente:
//+--------------------------------------------------------------------------------+
//| Costruttore default |
//+--------------------------------------------------------------------------------+
MyDateClass::MyDateClass(void)
{
//---
MqlDateTime mdt;
datetime t=TimeCurrent(mdt);
m_year=mdt.year;
m_month=mdt.mon;
m_day=mdt.day;
m_hour=mdt.hour;
m_minute=mdt.min;
m_second=mdt.sec;
Print(__FUNCTION__);
}
//+--------------------------------------------------------------------------------+
//| Costruttore parametrico |
//+--------------------------------------------------------------------------------+
MyDateClass::MyDateClass(int h,int m,int s)
{
MqlDateTime mdt;
datetime t=TimeCurrent(mdt);
m_year=mdt.year;
m_month=mdt.mon;
m_day=mdt.day;
m_hour=h;
m_minute=m;
m_second=s;
Print(__FUNCTION__);
}
|
Nel costruttore di default, tutti i membri della classe vengono riempiti con la funzione TimeCurrent(), nel costruttore parametrico solo i valori orari vengono compilati Altri membri della classe (m_year, m_month e m_day) viene automaticamente inizializzati con la data corrente.
Il costruttore predefinito ha uno scopo speciale durante l'inizializzazione di un array di oggetti della sua classe. Il costruttore, di cui tutti i parametri hanno valori predefiniti, non è un costruttore di default. Ecco un esempio:
//+--------------------------------------------------------------------------------+
//| La classe per il costruttore di default |
//+--------------------------------------------------------------------------------+
class CFoo
{
datetime m_call_time; // Ora della chiamata ultimo oggetto
public:
//--- Un costruttore con un parametro che ha un valore predefinito non è un costruttore di default
CFoo(const datetime t=0){m_call_time=t;};
//--- Un costruttore di copia
CFoo(const CFoo &foo){m_call_time=foo.m_call_time;};
string ToString(){return(TimeToString(m_call_time,TIME_DATE|TIME_SECONDS));};
};
//+--------------------------------------------------------------------------------+
//| Funzione di avvio del programma Script |
//+--------------------------------------------------------------------------------+
voidOnStart()
{
// CFoo foo; // Questa variante non può essere utilizzata - un costruttore di default non è impostato
//--- Le possibili opzioni per creare l'oggetto CFoo
CFoo foo1(TimeCurrent()); // Una chiamata esplicita di un costruttore parametrico
CFoo foo2(); // Una chiamata esplicita di un costruttore parametrico con un parametro di default
CFoo foo3=D'2009.09.09'; // Una chiamata implicita di un costruttore parametrico
CFoo foo40(foo1); // Una chiamata esplicita di un costruttore di copia
CFoo foo41=foo1; // Una chiamata implicita di un costruttore di copia
CFoo foo5; // Una chiamata esplicita di un costruttore di default (se non c'è costruttore di default,
// allora viene chiamato un costruttore parametrico con un valore predefinito)
//--- Le opzioni possibili per ricevere puntatori CFoo
CFoo *pfoo6=new CFoo(); // Creazione dinamica di un oggetto e la ricezione di un puntatore ad esso
CFoo *pfoo7=new CFoo(TimeCurrent());// Un'altra opzione di creazione di oggetti dinamici
CFoo *pfoo8=GetPointer(foo1); // Ora pfoo8 punta all'oggetto foo1
CFoo *pfoo9=pfoo7; // pfoo9 e pfoo7 puntano all'unico e stesso oggetto
// CFoo foo_array[3]; // Questa opzione non può essere utilizzata - un costruttore di default non è specificato
//--- Mostra il valore di m_call_time
Print("foo1.m_call_time=",foo1.ToString());
Print("foo2.m_call_time=",foo2.ToString());
Print("foo3.m_call_time=",foo3.ToString());
Print("foo4.m_call_time=",foo4.ToString());
Print("foo5.m_call_time=",foo5.ToString());
Print("pfoo6.m_call_time=",pfoo6.ToString());
Print("pfoo7.m_call_time=",pfoo7.ToString());
Print("pfoo8.m_call_time=",pfoo8.ToString());
Print("pfoo9.m_call_time=",pfoo9.ToString());
//--- Elimina array creati dinamicamente
delete pfoo6;
delete pfoo7;
//delete pfoo8; // Non è necessario eliminare pfoo8 in modo esplicito, in quanto indica l'oggetto creato automaticamente foo1
//delete pfoo9; // Non è necessario eliminare pfoo9 esplicitamente. in quanto punta allo stesso oggetto pfoo7
}
|
Se si toglie il commento alle stringhe
//CFoo foo_array[3]; // Questa variante non può essere utilizzata - un costruttore di default non è impostato
|
o
//CFoo foo_dyn_array[]; // Questa variante non può essere utilizzata - un costruttore di default non è impostato
|
il compilatore restituisce un errore per loro "il costruttore di default non è definito".
Se una classe ha un costruttore definito-dall'utente, il costruttore di default non viene generato dal compilatore. Ciò significa che se un costruttore parametrico è dichiarato in una classe, ma un costruttore di default non è dichiarato, non è possibile dichiarare gli array di oggetti di questa classe. Il compilatore restituirà un errore per questo script:
//+--------------------------------------------------------------------------------+
//| Una classe senza un costruttore di default |
//+--------------------------------------------------------------------------------+
class CFoo
{
string m_name;
public:
CFoo(string name) { m_name=name;}
};
//+--------------------------------------------------------------------------------+
//| Funzione di avvio del programma Script |
//+--------------------------------------------------------------------------------+
voidOnStart()
{
//--- Ottiene durante la compilazione l'errore "il costruttore di default non è definito"
CFoo badFoo[5];
}
|
In questo esempio, la classe CFoo dispone di un costruttore parametrico dichiarato - in questi casi, il compilatore non crea un costruttore di default automaticamente durante la compilazione. Allo stesso tempo, quando si dichiara un array di oggetti, si presume che tutti gli oggetti debbano essere creati ed inizializzati automaticamente. Durante l'auto-inizializzazione di un oggetto, è necessario chiamare un costruttore di default, ma dal momento che il costruttore di default non è esplicitamente dichiarato e non generato automaticamente dal compilatore, non è possibile creare un tale oggetto. Per questo motivo, il compilatore genera un errore in fase di compilazione.
C'è una sintassi speciale per inizializzare un oggetto utilizzando un costruttore. Inizializzatori Costruttore (costruzioni speciali per l'inizializzazione) per i membri di una struttura o classe, possono essere specificato nella lista di inizializzazione.
Un elenco di inizializzazione è un elenco di inizializzatori separati da virgole, che arriva dopo i due punti dopo la lista dei parametri di un costruttore e precede il corpo (va prima di una parentesi graffa di apertura). Ci sono diversi requisiti:
- Elenchi di inizializzazione possono essere utilizzati solo nei costruttori;
- Genitore membri non possono essere inizializzati nella lista di inizializzazione;
- L'elenco di inizializzazione deve essere seguito da una definizione (implementazione) di una funzione.
Ecco un esempio di diversi costruttori per inizializzare i membri della classe.
//+--------------------------------------------------------------------------------+
//| Una classe per memorizzare il nome di un personaggior |
//+--------------------------------------------------------------------------------+
class CPerson
{
string m_first_name; // Primo nome
string m_second_name; // Secondo nome
public:
//--- Un costruttore di default vuoto
CPerson() {Print(__FUNCTION__);};
//--- Un costruttore parametrico
CPerson(string full_name);
//--- Un costruttore con un elenco di inizializzazione
CPerson(string surname,string name): m_second_name(surname), m_first_name(name) {};
void PrintName(){PrintFormat("Name=%s Surname=%s",m_first_name,m_second_name);};
};
//+--------------------------------------------------------------------------------+
//| |
//+--------------------------------------------------------------------------------+
CPerson::CPerson(string full_name)
{
int pos=StringFind(full_name," ");
if(pos>=0)
{
m_first_name=StringSubstr(full_name,0,pos);
m_second_name=StringSubstr(full_name,pos+1);
}
}
//+--------------------------------------------------------------------------------+
//| Funzione di avvio del programma Script |
//+--------------------------------------------------------------------------------+
voidOnStart()
{
//--- Riceve un errore "il costruttore di default non è definito"
CPerson people[5];
CPerson Tom="Tom Sawyer"; // Tom Sawyer
CPerson Huck("Huckleberry","Finn"); // Huckleberry Finn
CPerson *Pooh = new CPerson("Winnie","Pooh"); // Winnie the Pooh
//--- Valori in Output
Tom.PrintName();
Huck.PrintName();
Pooh.PrintName();
//--- Eliminazione di un oggetto creato in modo dinamico
delete Pooh;
}
|
In questo caso, la classe CPerson ha tre costruttori:
- Un esplicito costruttore di default, che permette di creare un array di oggetti di questa classe;
- Un costruttore con un parametro, che ottiene un nome completo come parametro e lo divide per il nome e il secondo nome secondo lo spazio trovato;
- Un costruttore con due parametri che contiene un elenco di inizializzazione. Inizializzatori - m_second_name(surname) e m_first_name(name).
Si noti che l'inizializzazione utilizzando una lista ha sostituito un' assegnazione. I singoli membri devono essere inizializzati come:
class_member (una lista di espressioni)
|
Nella lista di inizializzazione, i membri possono andare in qualsiasi ordine, ma tutti i membri della classe verranno inizializzati secondo l'ordine del loro annuncio. Ciò significa che nel terzo costruttore, prima il membro m_first_name verrà inizializzato, come è annunciato prima, e solo dopo che m_second_name viene inizializzato. Ciò deve essere tenuto in considerazione nel caso in cui l'inizializzazione di alcuni membri della classe dipendono dai valori di altri membri della classe.
Se un costruttore di default non viene dichiarato nella classe base, ed al tempo stesso vengono dichiarati uno o più costruttori con parametri, si dovrebbe sempre chiamare uno dei costruttori della classe base nella lista di inizializzazione. Passa attraverso la virgola come membri ordinari della lista e verrà chiamato la prima volta durante l'inizializzazione dell'oggetto, non importa dove si trova nell'elenco di inizializzazione.
//+--------------------------------------------------------------------------------+
//| Classe Base |
//+--------------------------------------------------------------------------------+
class CFoo
{
string m_name;
public:
//--- Un costruttore con un elenco di inizializzazione
CFoo(string name) : m_name(name) { Print(m_name);}
};
//+--------------------------------------------------------------------------------+
//| Classe derivata da CFoo |
//+--------------------------------------------------------------------------------+
class CBar : CFoo
{
CFoo m_member; // Un membro della classe è un oggetto del genitore
public:
//--- Un costruttore di default nella lista di inizializzazione chiama il costruttore di un genitore
CBar(): m_member(_Symbol), CFoo("CBAR") {Print(__FUNCTION__);}
};
//+--------------------------------------------------------------------------------+
//| Funzione di avvio del programma Script |
//+--------------------------------------------------------------------------------+
voidOnStart()
{
CBar bar;
}
|
In questo esempio, quando si crea l'oggetto bar, verrà chiamato un costruttore di default CBar(), in cui prima viene chiamato un un costruttore per la CFoo padre, e poi arriva un costruttore per il membro della classe m_member.
Un distruttore è una funzione speciale che viene chiamata automaticamente quando un oggetto della classe viene distrutto. Il nome del distruttore è scritto come il nome della classe con una tilde (~). Stringhe, array dinamici ed oggetti, richiedenti la deinizializzazione, verranno de-inizializzati in ogni caso, indipendentemente dalla presenza o assenza del distruttore. Se c'è un distruttore, queste azioni verranno eseguite dopo aver chiamato il distruttore.
I distruttori sono sempre virtuali, indipendentemente dal fatto che siano dichiarati con la parola chiave virtual o meno.
Definizione dei Metodi della Classe
I metodi-funzione della classe possono essere definiti sia all'interno della classe che all'esterno della dichiarazione della classe. Se il metodo viene definito all'interno di una classe, allora il suo corpo viene subito dopo la dichiarazione del metodo.
Esempio:
class CTetrisShape
{
protected:
int m_type;
int m_xpos;
int m_ypos;
int m_xsize;
int m_ysize;
int m_prev_turn;
int m_turn;
int m_right_border;
public:
void CTetrisShape();
void SetRightBorder(int border) { m_right_border=border; }
void SetYPos(int ypos) { m_ypos=ypos; }
void SetXPos(int xpos) { m_xpos=xpos; }
int GetYPos() { return(m_ypos); }
int GetXPos() { return(m_xpos); }
int GetYSize() { return(m_ysize); }
int GetXSize() { return(m_xsize); }
int GetType() { return(m_type); }
void Left() { m_xpos-=SHAPE_SIZE; }
void Right() { m_xpos+=SHAPE_SIZE; }
void Rotate() { m_prev_turn=m_turn; if(++m_turn>3) m_turn=0; }
virtual void Draw() { return; }
virtual bool CheckDown(int& pad_array[]);
virtual bool CheckLeft(int& side_row[]);
virtual bool CheckRight(int& side_row[]);
};
|
Le funzioni da SetRightBorder (confine int) per Draw() vengono dichiarate e definite direttamente all'interno della classe CTetrisShape.
Il costruttore CTetrisShape() ed i metodi CheckDown(int& pad_array[]), CheckLeft(int& side_row[]) and CheckRight(int& side_row[]) vengono solo dichiarati all'interno della classe, ma non ancora definiti. Le definizioni di queste funzioni ci saranno ulteriormente, nel codice. Per definire il metodo all'esterno della classe, viene utilizzato l' operatore di risoluzione ambito, il nome della classe viene utilizzato come ambito.
Esempio:
//+--------------------------------------------------------------------------------+
//| Costruttore della classe base |
//+--------------------------------------------------------------------------------+
void CTetrisShape::CTetrisShape()
{
m_type=0;
m_ypos=0;
m_xpos=0;
m_xsize=SHAPE_SIZE;
m_ysize=SHAPE_SIZE;
m_prev_turn=0;
m_turn=0;
m_right_border=0;
}
//+--------------------------------------------------------------------------------+
//| Controlla l'abilità di spostarsi giù (per il "lungo" ed il cubo) |
//+--------------------------------------------------------------------------------+
bool CTetrisShape::CheckDown(int& pad_array[])
{
int i,xsize=m_xsize/SHAPE_SIZE;
//---
for(i=0; i<xsize; i++)
{
if(m_ypos+m_ysize>=pad_array[i]) return(false);
}
//---
return(true);
}
|
Modificatori di accesso Public, Protected e Private
Nello sviluppo di una nuova classe, si consiglia di limitare l'accesso ai membri dall'esterno. Per questo scopo vengono utilizzate le parole chiave private oprotected. In questo caso, i dati nascosti possono essere acceduti solo dai metodi-funzione della stessa classe. Se viene utilizzata la parola chiave protected, i dati nascosti possono essere acceduti anche dai metodi delle classi - eredi di questa classe. Lo stesso metodo può essere utilizzato per limitare l'accesso ai metodi-funzione di una classe.
Se è necessario aprire completamente l'accesso ai membri e/o metodi di una classe, utilizzare la parola chiave public.
Esempio:
class CTetrisField
{
private:
int m_score; // Punteggio
int m_ypos; // Posizione corrente delle figure
int m_field[FIELD_HEIGHT][FIELD_WIDTH]; // Matrice del pozzo
int m_rows[FIELD_HEIGHT]; // Numerazione delle righe del pozzo
int m_last_row; // Ultime righe libere
CTetrisShape *m_shape; // Figura Tetris
bool m_bover; // Game over
public:
void CTetrisField() { m_shape=NULL; m_bover=false; }
void Init();
void Deinit();
void Down();
void Left();
void Right();
void Rotate();
void Drop();
private:
void NewShape();
void CheckAndDeleteRows();
void LabelOver();
};
|
I membri della classe e metodi dichiarati dopo l'identificatore public: (e prima dello specificatore di accesso successivo) sono disponibili in qualsiasi riferimento all'oggetto classe dal programma. In questo esempio questi sono i seguenti membri: funzioni CTetrisField(), Init(), Deinit(), Down(), Left(), Right(), Rotate() and Drop().
I membri che vengono dichiarati dopo lo specificatore di accesso agli elementi private: (e prima dello specificatore di accesso successivo) sono disponibili solo per i membri funzioni di questa classe. Gli secificatori di accesso agli elementi finiscono sempre con i due punti (:) e possono comparire nella definizione della classe molte volte.
Qualsiasi membro della classe dichiarato dopo l' identificatore di accesso protected:(e fino al successivo identificatore di accesso) sono disponibili solo per le funzioni-membro di questa classe e per le funzioni-membro delle classi discendenti. Quando si tenta di fare riferimento ai membri che presentano gli specificatori private e protected dall'esterno, otteniamo l'errore della fase di compilazione. Esempio:
class A
{
protected:
// --- l'operatore copy è disponibile solo all'interno della classe A e suoi discendenti
void operator=(const A &)
{
}
};
class B
{
//--- oggetto della classe A dichiarato
A a;
};
//+------------------------------------------------------------------+
//| Funzione di avvio del programma di script |
//+------------------------------------------------------------------+
void OnStart()
{
//--- dichiara due variabili di tipo B.
B b1, b2;
//--- tenta di copiare un oggetto in un altro
b2=b1;
}
|
Durante la compilazione del codice, viene visualizzato il messaggio di errore - tentativo di chiamare l'operatore copy remoto:
attempting to reference deleted function 'void B::operator=(const B&)' trash3.mq5 32 6
|
La seconda stringa di seguito fornisce una descrizione più dettagliata — l'operatore copy nella classe B è stato eliminato esplicitamente, poiché viene chiamato l'operatore copy non disponibile della classe A:
function 'void B::operator=(const B&)' was implicitly deleted because it invokes inaccessible function 'void A::operator=(const A&)'
|
L'accesso ai membri della classe base può essere ridefinito duranteeredità nelle classi derivate.
'delete' specifier
L' identificatore delete contrassegna le funzioni dei membri della classe che non possono essere utilizzate. Ciò significa che se il programma fa riferimento a tale funzione in modo esplicito o implicito, l'errore viene già ricevuto in fase di compilazione. Ad esempio, questo identificatore consente di rendere i metodi parent non disponibili in una classe child. Lo stesso risultato può essere ottenuto se dichiariamo la funzione nell'area privata della classe genitrice (dichiarazioni nella sezione private). Qui, usare delete rende il codice più leggibile e gestibile a livello di discendenti.
class A
{
public:
A(void) {value=5;};
double GetValue(void) {return(value);}
private:
double value;
};
class B: public A
{
double GetValue(void)=delete;
};
//+------------------------------------------------------------------+
//| Funzione di avvio del programma di script |
//+------------------------------------------------------------------+
void OnStart()
{
//--- dichiara la variabile di tipo A.
A a;
Print("a.GetValue()=", a.GetValue());
//--- tenta di ottenere valore dalla variabile di tipo B.
B b;
Print("b.GetValue()=", b.GetValue()); // il compilatore mostra un errore in questa stringa
}
|
Il messaggio del compilatore:
attempting to reference deleted function 'double B::GetValue()'
function 'double B::GetValue()' was explicitly deleted here
|
Lo specificatore 'delete' consente di disabilitare il casting automatico o il costruttore copy, che altrimenti dovrebbe essere nascosto nella sezione private anche. Esempio:
class A
{
public:
void SetValue(double v) {value=v;}
//--- disabilita la chiamata di tipo int
void SetValue(int) = delete;
//--- disabilita l'operatore copy
void operator=(const A&) = delete;
private:
double value;
};
//+------------------------------------------------------------------+
//| Funzione di avvio del programma di script |
//+------------------------------------------------------------------+
void OnStart()
{
//--- dichiara due variabili di tipo A.
A a1, a2;
a1.SetValue(3); // errore!
a1.SetValue(3.14); // OK
a2=a1; // errore!
}
|
Durante la compilazione, riceviamo i messaggi di errore:
tentativodiriferimento ad una funzione 'void cancellataUna::funzione SetValue(int)'
'void A::SetValue(int)' was explicitly deleted here
attempting to reference deleted function 'void A::operator=(const A&)'
function 'void A::operator=(const A&)' was explicitly deleted here
|
Modificatore 'final' #
L'uso del modificatore 'final' durante la dichiarazione della classe vieta ulteriori eredità da questa classe. Se l'interfaccia della classe non richiede ulteriori modifiche, o le modifiche non sono ammesse per motivi di sicurezza, dichiarare questa classe con il modificatore 'final'. Inoltre, tutti i membri della classe saranno implicitamente considerati definitivi.
class CFoo final
{
//--- Corpo della classe
};
class CBar : public CFoo
{
//--- Corpo della classe
};
|
Se si tenta di ereditare da una classe con il modificatore 'final', come mostrato nell'esempio precedente, il compilatore restituirà un errore:
non si può ereditare da 'CFoo' siccome è stato dichiarato come 'final'
vedi dichiarazione di 'CFoo'
|
Unioni (union) #
Union è un tipo di dati speciale costituito da diverse variabili che condividono la stessa area di memoria. Pertanto, l'unione fornisce la capacità di interpretare la stessa sequenza di bit in due (o più) modi diversi. La dichiarazione dell'unione è simile alla dichiarazione della struttura ed inizia con la parola chiave union.
union LongDouble
{
long long_value;
double double_value;
};
|
A differenza della struttura, i vari membri sindacali appartengono alla stessa area di memoria. In questo esempio, l'unione di LongDouble è dichiarata con valori di tipo long e double che condividono la stessa area di memoria. Si prega di notare che è impossibile fare in modo che union memorizzi (a differenza di una struttura) simultaneamente valori integer long e valori real double , poiché le variabili long_value e double_value si sovrappongono (in memoria). D'altro canto, un programma MQL5 è in grado di elaborare in qualsiasi momento i dati contenenti nell'unione un valore intero(long) o reale(double). Pertanto, l'unione consente di ricevere due (o più) opzioni per rappresentare la stessa sequenza di dati.
Durante la dichiarazione dell' unione, il compilatore assegna automaticamente l'area di memoria sufficiente per memorizzare il tipo più grande (per volume) nella variabile union. La stessa sintassi viene utilizzata per accedere all'elemento union come per le strutture – operatore point.
union LongDouble
{
long long_value;
double double_value;
};
//+------------------------------------------------------------------+
//| Funzione start programma Script |
//+------------------------------------------------------------------+
void OnStart()
{
//---
LongDouble lb;
//--- ottiene e visualizza il numero -nan(ind) non valido
lb.double_value=MathArcsin(2.0);
printf("1. double=%f integer=%I64X",lb.double_value,lb.long_value);
//--- il più ampio valore normalizzato (DBL_MAX)
lb.long_value=0x7FEFFFFFFFFFFFFF;
printf("2. double=%.16e integer=%I64X",lb.double_value,lb.long_value);
//--- il più piccolo positivo normalizzato (DBL_MIN)
lb.long_value=0x0010000000000000;
printf("3. double=%.16e integer=%.16I64X",lb.double_value,lb.long_value);
}
/* Risultato dell'esecuzione
1. double=-nan(ind) integer=FFF8000000000000
2. double=1.7976931348623157e+308 integer=7FEFFFFFFFFFFFFF
3. double=2.2250738585072014e-308 integer=0010000000000000
*/
|
Poiché le unioni consentono al programma di interpretare gli stessi dati di memoria in modi diversi, esse vengono spesso utilizzate quando un' inusuale conversione di tipo è richiesta.
Le unioni non possono essere coinvolte nella eredità(inheritance), e loro anche non possono avere membri statici a causa della loro stessa natura. In tutti gli altri aspetti, l' unione si comporta come una struttura con tutti i suoi membri con un offset di zero. I seguenti tipi non possono essere i membri dell'unione:
Simillmente alle classi, l'unione è in grado di avere costruttori e distruttori, così come metodi. Per default, i membri dell'unione sono di tipo di accesso public. Per creare elementi privati, utilizzare la parola chiave private. Tutte queste possibilità sono visualizzate nell'esempio che illustra come convertire un colore del tipo color in ARGB come fa la funzione ColorToARGB().
//+------------------------------------------------------------------+
//| Unione per la conversione in colori (BGR) in ARGB |
//+------------------------------------------------------------------+
union ARGB
{
uchar argb[4];
color clr;
//--- costruttori
ARGB(color col,uchar a=0){Color(col,a);};
~ARGB(){};
//--- metodi pubblici
public:
uchar Alpha(){return(argb[3]);};
void Alpha(const uchar alpha){argb[3]=alpha;};
color Color(){ return(color(clr));};
//--- metodi privati
private:
//+------------------------------------------------------------------+
//| imposta il valore del canale alpha ed il colore |
//+------------------------------------------------------------------+
void Color(color col,uchar alpha)
{
//--- imposta il colore al membro clr
clr=col;
//--- imposta il valore del componente Alpha - livello d'opacità
argb[3]=alpha;
//--- scambia i bite dei componenti R e B (Red e Blue)
uchar t=argb[0];argb[0]=argb[2];argb[2]=t;
};
};
//+------------------------------------------------------------------+
//| Funzione start programma Script |
//+------------------------------------------------------------------+
void OnStart()
{
//--- 0x55 significa 55/255=21.6 % (0% - completamente trasparente)
uchar alpha=0x55;
//--- il tipo di colore è rappresentato come 0x00BBGGRR
color test_color=clrDarkOrange;
//--- qui vengono accettati i valori di byte provenienti dall'unione ARGB
uchar argb[];
PrintFormat("0x%.8X - qui è come il tipo 'color' appare per %s, BGR=(%s)",
test_color,ColorToString(test_color,true),ColorToString(test_color));
//--- Il tipo ARGB è rappresentato da 0x00RRGGBB, RR e BB vengono scambiati
ARGB argb_color(test_color);
//--- copia l'array di byte
ArrayCopy(argb,argb_color.argb);
//--- ecco come appare nella rappresentazione ARGB
PrintFormat("0x%.8X - rappresentazione ARGB con il canale alpha=0x%.2x, ARGB=(%d,%d,%d,%d)",
argb_color.clr,argb_color.Alpha(),argb[3],argb[2],argb[1],argb[0]);
//--- aggiungi livello opacità
argb_color.Alpha(alpha);
//--- prova a definire ARGB come tipo 'color'
Print("ARGB как color=(",argb_color.clr,") alpha channel=",argb_color.Alpha());
//--- copia l'array di byte
ArrayCopy(argb,argb_color.argb);
//--- ecco come appare nella rappresentazione ARGB
PrintFormat("0x%.8X - rappresentazione ARGB con il canale alpha=0x%.2x, ARGB=(%d,%d,%d,%d)",
argb_color.clr,argb_color.Alpha(),argb[3],argb[2],argb[1],argb[0]);
//--- controlla i risultati della funzione ColorToARGB()
PrintFormat("0x%.8X - risultato di ColorToARGB(%s,0x%.2x)",ColorToARGB(test_color,alpha),
ColorToString(test_color,true),alpha);
}
/* Risultato dell'esecuzione
0x00008CFF - qui è come il tipo color appare per clrDarkOrange, BGR=(255,140,0)
0x00FF8C00 - rappresentazione ARGB con il canale alpha=0x00, ARGB=(0,255,140,0)
ARGB as color=(0,140,255) alpha channel=85
0x55FF8C00 - rappresentazione ARGB con il canale alfa = 0x55, ARGB = (85,255,140,0)
0x55FF8C00 - result of ColorToARGB(clrDarkOrange,0x55)
*/
|
Interfacce #
Un'interfaccia consente la determinazione di specifiche funzionalità, che una classe può quindi implementare. Infatti, un'interfaccia è una classe che non può contenere membri, e non può avere un costruttore e/o un distruttore. Tutti i metodi dichiarati in una interfaccia sono puramente virtuali, anche in assenza di una definizione esplicita.
Un'interfaccia è definita utilizzando la parola chiave "interfaccia". Esempio:
//--- Interfaccia base per descrivere animali
interface IAnimal
{
//--- I metodi dell'interfaccia hanno accesso pubblico per default
void Sound(); // Il suono prodotto dall'animale
};
//+------------------------------------------------------------------+
//| The classe CCat è ededitata dalla classe IAnimal interface |
//+------------------------------------------------------------------+
class CCat : public IAnimal
{
public:
CCat() { Print("Il gatto è nato"); }
~CCat() { Print("Il gatto è morto"); }
//--- Implementare il metodo Sound di IAnimal interface
void Sound(){ Print("meou"); }
};
//+------------------------------------------------------------------+
//| La classe CDog è ereditata dalla classe IAnimal interface |
//+------------------------------------------------------------------+
class CDog : public IAnimal
{
public:
CDog() { Print("Il cane è nato"); }
~CDog() { Print("Il cane è morto"); }
//--- Implementare il metodo Sound di IAnimal interface
void Sound(){ Print("guaf"); }
};
//+------------------------------------------------------------------+
//| Programma Script funzione start |
//+------------------------------------------------------------------+
void OnStart()
{
//--- Un array di puntatori a oggetti del tipo IAnimal
IAnimal *animals[2];
//--- La creazione di classi figlio di IAnimal e risparmio di puntatori a loro, in un array
animals[0]=new CCat;
animals[1]=new CDog;
//--- Chiamata al metodo Suono() dell'interfaccia base IAnimal per ogni bambino
for(int i=0;i<ArraySize(animals);++i)
animals[i].Sound();
//--- Eliminazione oggetti
for(int i=0;i<ArraySize(animals);++i)
delete animals[i];
//--- Risultato dell'esecuzione
/*
Il gatto è nato
Il cane è nato
meou
guaf
Il gatto è morto
Il cane è morto
*/
}
|
Come le classi astratte, un oggetto interfaccia non può essere creato senza eredità. Un'interfaccia può essere ereditata solo dalle altre interfacce e può essere un genitore per una classe. Un interfaccia è sempre con visibilità pubblica.
Un'interfaccia non può essere dichiarata all'interno di una dichiarazione della classe o di una struttura, ma un puntatore per l'interfaccia può essere salvato in una variabile di tipo void *. In generale, un puntatore ad un oggetto di qualsiasi classe può essere salvato in una variabile di tipo void *. Per convertire un puntatore void * a un puntatore ad un oggetto di una classe particolare, utilizzare l'operatore dynamic_cast. Se la conversione non è possibile, il risultato dell'operazione dynamic_cast sarà NULL.
Vedi anche
Programmazione Ad Oggetti (OOP Object-Oriented-Programming)