next up previous contents index
Next: Argomenti da linea di Up: Approfondimenti. I files d'intestazione Previous: Typedef   Indice   Indice analitico

Argomenti con valori default

Abbiamo già visto che le funzioni in C++ possono essere sovrapposte: è possibile cioè avere più funzioni facenti riferimento ad un medesimo indentificatore; esse devono tuttavia differire nei tipi o nel numero degli argomenti. Vediamo il seguente esempio di sovrapposizione di funzioni:

// ex8_2_1
#include <iostream.h>

void stampa (int* v, int n, int colonne) {
  for (int i = 0; i < n; i++)
    if ((i+1) % colonne == 0)
      cout << v[i] << "\n";
    else
      cout << v[i] << "\t";
}

void stampa (int* v, int n) {
  for (int i = 0; i < n; i++)
    if ((i+1) % 5 == 0)
      cout << v[i] << "\n";
    else
      cout << v[i] << "\t";
}

void main() {
  int array[10];
  for (int i = 0; i < 10; i++)
    array[i] = i+1;
  stampa (array, 10, 5);
  stampa (array, 10);
}

output:
 1 2 3 4 5
 6 7 8 9 10
 1 2 3 4 5
 6 7 8 9 10

Piuttosto che avere due funzioni del tutto simili, possiamo utilizzare la tecnica degli argomenti default per il terzo argomento:


void stampa (int* v, int n, int colonne = 5) {
  for (int i = 0; i < n; i++)
    if ((i+1) % colonne == 0)
      cout << v[i] << "\n";
    else
      cout << v[i] << "\t";
}

Cosa vuol dire int colonne = 5? Significa che se viene fornito il terzo argomento in fase di chiamata allora il valore 5 non viene preso in considerazione; se invece vengono passati due soli parametri locali allora automaticamente colonne viene inizializzato con il valore 5.

Non c'è nessun limite al numero di elementi default che una funzione può avere; ci sono solo due regole da rispettare: gli argomenti default non devono creare ambiguità con altre funzioni sovrapposte; possono avere valori default solo gli argomenti all'estrema destra nella dichiarazione di funzione, senza saltarne nessuno. Vediamo qualche esempio. Le seguenti due dichiarazioni di funzioni sono errate se utilizzate nello stesso programma:


void funzione (int arg1, int arg2 = 0);
void funzione (int arg1);
Infatti, se si avesse una chiamata come funzione(7) il compilatore non potrebbe scelgliere la funzione da utilizzare; ricordiamo che in ogni caso il C++ non ammette ambiguità nelle chiamate di funzione. Anche la seguente dichiarazione è errata:

void funzione (int arg1 = 0, int arg2);
perché, come detto, gli argomenti con valori default devono essere dichiarati a partire dall'estrema destra. Data la seguente dichiarazione

void funzione (int arg1 = 5, int arg2 = 12);
sono invece corrette le seguenti chiamate:

funzione ();      // arg1 = 5, arg2 = 12
funzione (3);     // arg1 = 3, arg2 = 12
funzione (3, 7);  // arg1 = 3, arg2 = 7
Utilizzeremo spesso argomenti con valore default nei costruttori delle classi; vediamo un esempio con il costruttore di una struttura:

// ex8_2_3
#include <iostream.h>
#include <string.h>

enum Linea { CONTINUA, TRATTEGGIATA, PUNTOLINEA};
enum Colore { NERO, BIANCO, ROSSO, VERDE, BLU };

struct Circonferenza {
  double x, y;  // posizione del centro
  double raggio;
  char* nome;
  Colore coloreLinea, coloreRiempimento;
  Linea stileLinea;
  bool soloContorno;
  Circonferenza ( double Raggio,
                  const char* Nome         = "senzanome",
                  double X                 = 0,
                  double Y                 = 0,
                  Colore ColoreLinea       = NERO,
                  Colore ColoreRiempimento = BIANCO,
                  Linea StileLinea         = CONTINUA,
                  bool SoloContorno        = true ) {
    raggio = Raggio;
    nome = new char[strlen(Nome) + 1];
    strcpy (nome, Nome);
    x = X; y = Y;
    coloreLinea = ColoreLinea;
    coloreRiempimento = ColoreRiempimento;
    stileLinea = StileLinea;
    soloContorno = SoloContorno;
  }       
};


void stampa (const Circonferenza& c) {
  cout << "\nnome: " << c.nome << "\n" <<
    "(" << c.x << " , " << c.y << ")" <<
    " raggio: " << c.raggio << "\n" <<
    "colore linea: " << c.coloreLinea << "\n" <<
    "colore riempimento: " << c.coloreRiempimento << "\n" <<
    "stile linea: " << c.stileLinea << "\n" <<
    "solo contorno: " << c.soloContorno << "\n";
}

void main() {
  Circonferenza c1 ( 2.5, "circ.1",
                     2, -1.5,
                     ROSSO, VERDE,
                     TRATTEGGIATA, false);
  Circonferenza c2 (6, "circ.2", 7, 1.2);
  Circonferenza c3 (5);
  stampa (c1); stampa (c2); stampa (c3);
}

output:
nome: circ.1
(2 , -1.5) raggio: 2.5
colore linea: 2
colore riempimento: 3
stile linea: 1
solo contorno: 0

nome: circ.2
(7 , 1.2) raggio: 6
colore linea: 0
colore riempimento: 1
stile linea: 0
solo contorno: 1

nome: senzanome
(0 , 0) raggio: 5
colore linea: 0
colore riempimento: 1
stile linea: 0
solo contorno: 1

L'esempio precedente potrebbe essere parte di una qualunque libreria grafica; gli argomenti con valori default ci offrono la possibilità di creare una Circonferenza senza fornire tutti i parametri: l'unico argomento che deve necessariamente essere passato è il raggio, per ovvi motivi.

ex-2
sulla falsa riga dell'esempio ex8_2_3 si costruisca una struttura Rettangolo e una funzione stampa di uscita delle caratteristiche del rettangolo passatogli; Rettangolo deve essere passato per riferimento costante: perché?


next up previous contents index
Next: Argomenti da linea di Up: Approfondimenti. I files d'intestazione Previous: Typedef   Indice   Indice analitico
Claudio Cicconetti
2000-09-06