Come lavorare con le proprietà di tipo stringa o array a lunghezza variabile

Nella introduzione alla programmazione ad oggetti in FreeBASIC abbiamo visto cosa sono le proprietà di un oggetto.

In questo nuovo articolo proveremo a scendere un po’ più nel dettaglio per imparare come il compilatore FreeBASIC tratta quel tipo di proprietà che hanno, per loro stessa natura, una lunghezza variabile: ovvero una quantità variabile di spazio di memoria occupato.

Vedremo quindi come lavorare con le stringhe e gli array di tipo variabile (variable-length array).

Lavorare con gli array di tipo variabile

In FreeBASIC le classi sono realizzate attraverso dei tipi di dato definiti da noi (user-defined type o UDT) che devono avere una dimensione fissa.

Nonostante questo, possiamo scegliere come proprietà un array di tipo variabile.

Per farlo dovremo però dichiarare, all’interno della definizione della classe, il nostro array dinamico utilizzando la parola-chiave any.

Questa parola-chiave comunica al compilatore FreeBASIC – come fosse un segnaposto – che le effettive dimensioni dell’array saranno specificate attraverso l’istruzione redim soltanto dopo aver creato un oggetto (istanza della nostra classe).

Esempio con una proprietà stringa e un array di tipo variabile

''definizione della classe
type mia_classe
  ''definizione delle proprietà
  dim as string mia_stringa_variabile
  dim as integer mio_array_variabile(any)
end type

''creiamo un oggetto del tipo della classe definita
dim as mia_classe mio_oggetto

''aggiorniamo la proprietà stringa
mio_oggetto.mia_stringa_variabile = "Oggi e' una bella giornata!"

''aggiorniamo la proprietà array
redim mio_oggetto.mio_array_variabile(1 to 5)

''stampa la stringa
print mio_oggetto.mia_stringa_variabile
''stampa la dimensione dell'array
print str(ubound(mio_oggetto.mio_array_variabile) - _
          lbound(mio_oggetto.mio_array_variabile) + 1)

Nel prossimo articolo vedremo come in FreeBASIC viene implementato il concetto di incapsulamento, tipico della programmazione orientata agli oggetti, per una migliore gestione dell’accesso ai membri di una classe.

Introduzione alla programmazione orientata agli oggetti in FreeBASIC

Ebbene sì! Anche con FreeBASIC possiamo adottare nei nostri programmi il paradigma della programmazione orientatata agli oggetti.

In inglese: object-oriented programming (OOP).

Classe

Il costrutto della classe lo realizzaremo in FreeBASIC attraverso un tipo di dato definito da noi (user-defined type o UDT) che utilizzeremo quindi come modello per creare i nostri oggetti.

Oggetto

Per poter creare un oggetto a partire da una classe – ovvero, in FreeBASIC, dal tipo di dato definito da noi – questa dovrà contenere tra i suoi membri, oltre alle proprietà (variabili, costanti, array, ecc.) e ai metodi (subroutines e funzioni), anche delle particolari procedure chiamate costruttore e distruttore.

Si usa dire che un oggetto è una istanza di una classe.

Costruttore e Distruttore

Quando viene definito un nuovo oggetto, il compilatore FreeBASIC chiamata automaticamente la procedura costruttore di default che inizializza i valori dei parametri dell’oggetto.

Quando invece viene eliminato un oggetto, il compilatore FreeBASIC chiama automaticamente la procedura distruttore che libera le risorse del sistema precedentemente utilizzate dall’oggetto.

Un oggetto viene implicitamente distrutto ogni volta che viene passato per valore in una procedura o quando esce dal suo ambito di utilizzo.

Per dichiarare una certa procedura come costruttore del nostro oggetto utilizzeremo l’istruzione constructor, mentre per la procedura distruttore utilizzeremo l’istruzione destructor.

A differenza delle procedure sub o function, per il costruttore e il distruttore non è necessario specificare alcun nome.

In una classe possiamo specificare più costruttori, ma un solo distruttore.

Quando invece copiamo o creiamo un clone di un oggetto a partire da un oggetto dello stesso tipo il compilatore FreeBASIC crea automaticamente anche il costruttore per la copia o la clonazione.

Queste due tipologie di costruttore possono però anche essere dichiarate e definite dallo stesso programmatore; il quale può, inoltre, anche dichiarare e definire eventuali altri costruttori “specializzati”.

Vediamo ora un po’ più in dettaglio alcune caratteristiche di queste diverse tipologie di costruttori.

Costruttore di default

Il costruttore di default è una procedura che non riceve argomenti e quindi eventuali parametri al suo interno avranno dei valori già impostati da noi o, se non li abbiamo impostati, avranno dei valori di default (p.e. le variabili numeriche avranno valore zero). Questo costruttore viene invocato dal compilatore FreeBASIC appena si definisce un oggetto.

Costruttore per le copie o i cloni degli oggetti

Questo costruttore viene invocato automaticamente dal compilatore FreeBASIC quando viene creato o clonato un oggetto da parte di un altro oggetto dello stesso tipo, oppure quando viene passato ad una procedura per valore (byval). Per dichiarare questo tipo di costruttore occorre che nella dichiarazione sia previsto come argomento un oggetto dello stesso tipo passato per riferimento.

Costruttore “specializzato”

Come programmatori, possiamo definire uno o più di questo tipo di costruttore quando, per esempio, abbiamo la necessità di passare dei valori specifici come argomenti in fase di creazione dell’oggetto.

Esempio dei tre tipi di costruttore e del distruttore

type poligono_regolare
  declare constructor
  declare constructor(byref p as poligono_regolare)
  declare constructor(n as integer)
  declare destructor
  dim numero_lati as integer
end type

''definizione della procedura costruttore di default
constructor poligono_regolare
  numero_lati = 3
end constructor

''definizione della procedura costruttore per le copie o i cloni
constructor poligono_regolare(byref p as poligono_regolare)
  numero_lati = p.numero_lati
end constructor

''definizione della procedura per un costruttore "specializzato"
constructor poligono_regolare(n as integer)
  numero_lati = n
end constructor

''definizione della procedura distruttore
destructor poligono_regolare
  ''istruzioni per la distruzione (vedi istruzione delete) per esempio
  ''di eventuali altri oggetti utilizzati all'interno dell'oggetto.
end destructor

Proprietà

Come abbiamo visto, a partire da una classe possiamo creare (istanziare) più oggetti. Quando creiamo degli oggetti, è molto probabile che ci interessi caratterizzarli con differenti proprietà. Rimanendo nell’esempio della classe “poligono_regolare”, magari vorremmo creare diversi poligoni regolari (oggetti) con la proprietà “colore” differente. Nella programmazione ad oggetti è bene mantenere privato l’ambito di utilizzo delle proprietà: in modo tale che non sia possibile accederci direttamente dall’esterno. Per modificarne il valore, o per leggerlo, scriveremo delle procedure ad hoc che ci garantiscano anche un maggior controllo sull’input dell’utente e sul tipo di output.

Esempio sulle proprietà

type poligono_regolare
  ''[...]
  declare property colore(byref c as ulong) ''per impostare il valore del colore
  declare property colore() as ulong ''per ottenere il valore del colore
  private:
    dim mio_colore as ulong
end type

property poligono_regolare.colore(byref c as ulong)
  this.mio_colore = c
end property

property poligono_regolare.colore() as ulong
  return this.mio_colore
end property

Nota sul parametro this

Nell’esempio riportato più sopra abbiamo fatto uso della parola-chiave this.

Questa parola-chiave è un argomento nascosto passato per riferimento che funge appunto da riferimento ad uno specifico oggetto (ovvero alla specifica istanza di una classe). Questo riferimento viene automaticamente passato a tutti i membri dichiarati nella classe: costruttori, distruttore, proprietà, metodi (subroutines e funzioni) in modo tale che le loro definizioni potranno agire su quello specifico oggetto.

Metodi

Come per i costruttori e il distruttore, anche per i metodi, che in ultima analisi non sono altro che delle procedure di tipo sub o function, occorre che siano dichiarati all’interno della classe e definiti all’esterno.

Esempio di metodi

type poligono_regolare
  ''[...]
  declare sub angolo_al_centro(n as integer)
  declare function tipo(n as integer) as string
  ''[...]
  dim angolo as long
end type

sub poligono_regolare.angolo_al_centro(n as integer)
  angolo = 360/n
  print "Angolo al centro: "; angolo
end sub

function poligono_regolare.tipo(n as integer) as string
  select case n
    case 3
      return "triangolo equilatero"
    case 4
      return "quadrato"
    case 5
      return "pentagono regolare"
    case else
      return "non gestito"
  end select
end function

Come creare un oggetto e una sua copia

Ora che abbiamo le basi per scrivere una classe, vediamo come creare il nostro primo oggetto e come da questo fare una sua copia.

''Definisce la CLASSE
type poligono_regolare
  declare constructor() ''costruttore di default
  declare constructor(byref p as poligono_regolare) ''costruttore per copia/clone
  declare property numero_lati(byref n as integer) ''per impostare/cambiare
                                                   ''il numero dei lati
  declare property numero_lati() as integer ''per ottenere il numero dei lati
  private:
    dim num_lati as integer
end type

constructor poligono_regolare
  num_lati = 3
end constructor

constructor poligono_regolare(byref p as poligono_regolare)
  num_lati = p.num_lati
end constructor

property poligono_regolare.numero_lati(byref n as integer)
  this.num_lati = n
end property

property poligono_regolare.numero_lati() as integer
  return this.num_lati
end property

''------------------------------

''Dimensiona e crea un nuovo OGGETTO (una istanza della CLASSE)
dim poligono_a as poligono_regolare
print poligono_a.numero_lati ''stampa il valore di default 3
poligono_a.numero_lati = 6 ''cambia il numero dei lati

''Crea una copia
dim poligono_b as poligono_regolare
print poligono_b.numero_lati ''stampa il valore di default 3
poligono_b = poligono_a ''crea la copia
print poligono_b.numero_lati ''stampa il valore 6

Bene! Come introduzione alla programmazione orientata agli oggetti in FreeBASIC possiamo fermarci qui. 🙂

Nel prossimo articolo vedremo come lavorare con le proprietà di tipo stringa o array a lunghezza variabile.