PROGRAMMAZIONE AD OGGETTI 2001-2002

14-1-2002

STATIC
NAMESPACES
COPY CONSTRUCTORS

 

STATIC

La parola chiave static assume diversi significati a seconda del contesto in cui è utilizzata.

Una variabile dichiarata static all'interno di una funzione (static int a;) è una variabile il cui valore non viene memorizzato nello stack della funzione, ma nella zona in cui sono memorizzate anche le variabili globali (heap: zona di memoria riservata per l'esecuzione del programma): in questo modo la variabile è comunque locale (può essere utilizzata solo all'interno della funzione) ma il suo valore non viene cancellato alla chiusura della funzione (e quindi alla distruzione dello stack) ma "sopravvive", e rimane immutato per una successiva chiamata di funzione. Così la variabile static all'interno di una funzione viene inizializzata la prima volta che la funzione viene chiamata, dopo di che il suo valore è riutilizzabile (e modificabile) con successive chiamate alla funzione. Attenzione: non è una variabile globale (anche se rimane per tutta la durata del programma), perché ha visibilità globale.

  vedi esempio StaticVariablesInFunctions.cpp  

Un oggetto può essere dichiarato static come una variabile all'interno di una funzione: in questo modo gli attributi dell'oggetto non vengono cancellati alla chiusura della funzione; inoltre il costruttore viene richiamato solo la prima volta, mentre nelle successive chiamate a funzione il costruttore dell'oggetto static non viene più richiamato.

  vedi esempi: StaticObjectsInFunctions.cpp  
    StaticDestructors.cpp  

Nel caso in cui il codice di un programma sia costituito da più file, è possibile usare una variabile (globale) in un file che è definita in un altro file. Questo è possibile specificando al compilatore che la variabile che si vuole usare è definita all'interno di un altro file, linkato a quello corrente, con la keyword extern. Ad esempio: per usare dentro il file "file2.cpp" la variabile "a" definita nel file "file1.cpp" bisogna scrivere "extern a". Questo è possibile solo se "a" è una variabile globale nel file "file1.cpp". Se una variabile globale è definita static questo vuol dire che non può assolutamente essere utilizzata all'esterno del file in cui è dichiarata. Questo uso di static è sconsigliato nella programmazione di C++.

  vedi esempi: LocalExtern.cpp  
    LocalExtern2.cpp  

E' possibile definire static anche una variabile all'interno di una classe: in questo caso la variabile è in comune a tutti gli oggetti istanziati a patire da quella classe. Più precisamente: la zone di memoria di quella variabile è la stessa per tutti gli oggetti. La variabile static può essere anche private: in questo caso è visibile solamente agli oggetti della classe e non a funzioni esterne. Una variabile di questo tipo deve essere obbligatoriamente inizializzata prima della creazione di un oggetto con la sintassi:

class X
{

static int b;  
/* è privata */

public:

...

};

...

int X::b = 0; // inizializzazione

L'inizializzazione fatta in questo modo può essere eseguita una sola volta: dopo l'inizializzazione la variabile b diventa a tutti gli effetti private e non può più essere modificata dall'esterno della classe (come invece avviene con il comando "int X::b = 0;"). Una variabile static può essere utile per sapere quanti oggetti della stessa classe sono stati istanziati: ogni oggetto può accedere a questa informazione se è contenuta in una variabile static, che viene aggiornata dal costruttore ogni volta che un nuovo oggetto viene istanziato.

  vedi esempi: Statinit.cpp  
    Quanti.cpp  

E' possibile anche definire un metodo static, ottenendo così un metodo che agisce su tutti gli oggetti della classe. Naturalmente questo metodo può lavorare solo con le variabili della classe che sono definite static (non può utilizzare le normali variabili degli oggetti perché queste hanno un valore diverso in ogni oggetto). Inoltre può richiamare solo altri metodi static. Il metodo static può essere richiamato da ogni singolo oggetto (con l'operatore "."), oppure con una sintassi del tipo:

class X
{

...

public:

static int f();
/* dichiarazione metodo static */

...

};

...

X::f;
/* chiamata a metodo static */

In questo modo si specifica che il metodo è static e agisce su tutti gli oggetti della classe X.

  vedi esempio StaticMemberFunctions.cpp  

HOME


NAMESPACES

E' possibile raggruppare delle dichiarazioni di variabili, funzioni o classi all'interno di namespaces: in questo modo si possono dichiarare gli stessi nomi all'interno di namespaces diversi, per poi utilizzarli specificando il namespace si appartenenza. Ecco la sintassi:

Nel file header "header1.h":

namespace Gruppo1
{

int x;  
/* dichiarazioni interne a Gruppo1 */

int f();

...

}

Una volta incluso il file header all'interno di un file .cpp, è possibile utilizzare le dichiarazioni interne al namespace in tre modi diversi. Si può utilizzare l'operatore "::" (come per le classi):

Gruppo1::x = 1;

In questo modo, ogni volta che si utilizza la variabile x bisogna specificare che appartiene al namespace Gruppo1 (e non è una variabile x definita all'interno del file .cpp o un'altra variabile x contenuta in un altro namespace).

  vedi esempio ScopeResolution.cpp  

E' possibile utilizzare la direttiva:

using namespace Gruppo1;

x = 2;

x = f();

In questo modo tutte le dichiarazioni contenute all'interno del namespace Gruppo1 possono essere utilizzate senza specificare che appartengono al namespace Gruppo1.

  vedi esempi: NameSpaceInt.h  
    NameSpaceMath.h  
    Arithmetic.cpp  

C'è un altro modo di utilizzare una variabile contenuta nel namespace con la dichirazione (diversa dalla direttiva):

using Gruppo1::x;

x = 1;

x = Gruppo1::f();

In questo modo solo la variabile x può essere utilizzata senza specificare l'appartenenza al namespace Gruppo1 (bisogna invece specificarlo per f()).

  vedi esempi: UsingDeclaration.h  
    UsingDeclaration1.cpp  
    UsingDeclaration2.cpp  

E' molto utile dichiarare le variabili extern all'interno di un namespace in modo da evitare l'uso di static per le variabili globali. Si possono utilizzare namespaces all'interno di altri namespaces in modo gerarchico. Inoltre un namespace già esistente può essere ampliato con altre dichiarazioni in un altro file header dove è definito un namespace con lo stesso nome:

Nel file header "header2.h"

namespace Gruppo1
{

extern int y;
int g();
class Y;
...

}

Una classe dichiarata all'interno del namespace può essere definita fuori dal namespace all'interno del file header.

HOME


COPY CONSTRUCTORS

Quando si passa ad una funzione un oggetto per valore, il compilatore fa una copia fisica dell'oggetto sullo stack (senza chiamare il costruttore della classe). La copia dell'oggetto passato, fatta automaticamente dal compilatore, può generare dei problemi con contatori o puntatori. Per controllare come viene eseguita la copia di un oggetto si può definire un costruttore di copia (copy constructor) che agisce in modo opportuno. Il costruttore di copia, che viene quindi definito dal programmatore, agisce in modo "intelligente"; in assenza del costruttore di copia il compilatore fa la copia fisica byte per byte. Con il costruttore di copia si può creare un oggetto uguale ad un altro già esistente come una semplice assegnazione di variabile:

A a1(2);
/* oggetto di classe A creato con il costruttore */

A a2 = a1;
/* a2 è creato dal costruttore di copia che prende come parametro a1 */

Il copy constructor di una classe, deve ricevere come parametro un puntatore ad un oggetto di quella classe che è passato come costante:

class A
{

...
A(const &A from);
/* costruttore di copia */
...

};

Il compilatore riconosce il costruttore di copia dal parametro che questo prende. Se non si vuole che un oggetto venga copiato basta definire un costruttore di copia che non fa nulla, ma che impedisce al compilatore di fare la copia byte per byte di default.

  vedi esempi: CopyConst.cpp  
    CopyConst2.cpp  
    HowMany.cpp  
    HowMany2.cpp  

HOME