next up previous contents index
Next: Array, enumerati, stringhe Up: Puntatori e riferimenti Previous: Riferimenti   Indice   Indice analitico

Sovrapposizione di funzioni

Concludiamo il capitolo con un argomento che non riguarda puntatori e riferimenti: la sovrapposizione delle funzioni (function overloading). Si tratta di una comoda flessibilità del linguaggio C++, che trova applicazione immediata in moltissimi programmi. Vediamo qual è la necessità che spinge un programmatore a ``sovrapporre'' le sue funzioni; supponiamo di avere un programma matematico, il quale ha come scopo quello di effettuare la divisione tra due numeri. Una funzione adatta allo scopo potrebbe essere la seguente:

int div (int dividendo, int divisore) {
  return dividendo / divisore;
}
la quale ha però lo svantaggio di permettere la divisione solo tra due numeri interi; dovremmo allora aggiungere la funzione

double divReale (double dividendo, double divisore) {
  return dividendo / divisore;
}
Ancora: se volessimo poi dividere un intero per un reale, o viceversa? Ci troveremmo a dovere aggiungere ancora due funzioni:

double divIntReale (int dividendo, double divisore) {
  return double(dividendo) / divisore;
}
double divRealeInt (double dividendo, int divisore) {
  return dividendo / double(divisore);
}
Finalmente abbiamo finito, e ci troviamo con ben quattro funzioni, avente tutti nomi diversi (div,divReale,divRealeInt,divIntReale), che ben presto confonderemo. Questo modo di procedere è molto scomodo, sebbene sia utilizzato comunemente in linguaggi diversi dal C++, i quali non permettono la sovrapposizione delle funzioni. In C++ invece si identificano tutte le funzioni con il medesimo nome, poi ci penserà il compilatore a scegliere la funzione appropriata. Bellissimo: le funzioni per dividere due numeri (interi o reali) restano quattro, ma il nome è unico, così la probabilità di fare confusione tra i diversi nomi è ridotta a zero. Avremo allora:

// ex5_6_1.cpp
#include <iostream.h>

int div (int divisore, int dividendo) {
  cout << "\ndivisione tra due interi: ";
  return divisore / dividendo;
}

double div (double divisore, double dividendo) {
  cout << "\ndivisione tra due reali: ";
  return divisore / dividendo;
}

double div (int divisore, double dividendo) {
  cout << "\ndivisione intero per reale: ";
  return double(divisore) / dividendo;
}

double div (double divisore, int dividendo) {
  cout << "\ndivisione reale per intero: ";
  return divisore / double(dividendo);
}

void main() {
  cout << div (15,   3);
  cout << div (37.5, 2.0);
  cout << div (37,   2.0);
  cout << div (37.5, 2);
}

output:
divisione tra due interi: 5
divisione tra due reali: 18.75
divisione intero per reale: 18.5
divisione reale per intero: 18.75

All'interno di ciascuna funzione abbiamo inserito un comando di stampa, al fine di evidenziare quale è in realtà la funzione chiamata dal compilatore, in maniera del tutto automatica. Naturalmente non solo le funzioni matematiche possono essere sovrapposte; vediamo due esempi di sovrapposizione di funzioni non matematiche:


// ex5_6_2.cpp
// modifica dell'esempio ex5_5_4.cpp
#include <iostream.h>

void scambia (int* a, int* b) {
  int t = *a;
  *a = *b;
  *b = t;
}

void scambia (int& a, int& b) {
  int t = a;
  a = b;
  b = t;
}

void main() {
  int a = 3, b = 7;
  cout << "a = " << a << "\tb = " << b << "\n";
  scambia (&a, &b);
  cout << "a = " << a << "\tb = " << b << "\n";
  scambia (a, b);
  cout << "a = " << a << "\tb = " << b << "\n";
}

output:
 a = 3 b = 7
 a = 7 b = 3
 a = 3 b = 7

Come si vede un programma che sovrappone le funzioni, risulta molto più leggibile e semplice da utilizzare di uno ad esso equivalente, che abbia nomi diverse per funzioni che svolgono la stessa mansione. Andiamo avanti.


// ex5_6_3.cpp
#include <iostream.h>
#include <math.h>

void messaggio () {
  cout << "benevenuto!\n"
    "questo programma calcola la radice quadrata\n"
    "algebrica di un numero reale dato\n"
    "scrivi un numero negativo per terminare";
}

void messaggio (double x) {
  cout << "l'unica radice e': " << x;
}

void messaggio (double x1, double x2) {
  cout << "le radici sono: " << x1 << " e " << x2;
}

void main() {
  double argomento;
  messaggio();
  do {
    cout << "\n? "; cin >> argomento;
    if (argomento > 0) {
      double x1 = sqrt (argomento);
      double x2 = -x1;
      messaggio (x1, x2);
    }
    else if (argomento == 0)
      messaggio (0);
  } while (argomento >= 0);
}

esempio di output:
benevenuto!
questo programma calcola la radice quadrata
algebrica di un numero reale dato
scrivi un numero negativo per terminare
? 9
le radici sono: 3 e -3
? 7
le radici sono: 2.64575 e -2.64575
? 0
l'unica radice e': 0
? -1

In questo esempio la sovrapposizione non è avvenuta in base al tipo degli argomenti, bensì secondo il loro numero. Si noti che funzioni sovrapposte possono avere tipo di ritorno diversi, ma non è possibile sovrapporre due funzioni che differiscano solo nel ritorno, perché potrebbero crearsi ambiguità nelle chiamate di funzione. Vediamo un ultimo esempio:


// ex5_6_4.cpp
#include <iostream.h>
#include <math.h>

const double PI = 3.14159265358979323846264338327;

// gg = gradi, mm = minuti, ss = secondi
double sin (int gg, int mm, int ss) {
  double gradiDecimali = gg + 
    double(mm) / 60.0 + 
    double(ss) / 3600.0;
  double radianti = gradiDecimali * PI / 180;
  return sin(radianti);
}

void main() {
  cout << sin (PI / 6) << "\n";   // .5
  cout << sin (28, 60, 3600);     // .5
}

Questo esempio mostra come sia possibile sovrapporre anche le funzioni di libreria (nel caso specifico la sin), per meglio adattarle agli scopi dei nostri programmi.

ex-2
si scriva un programma avente tre funzioni sovrapposte che calcolino il minore tra due numeri dati come argomento; nella prima il passaggio avvenga per valore, nella seconda per indirizzo (costante?) e nella terza per riferimento (costante?);
ex-3
si scriva la sovrapposizione delle funzioni della libreria math.h per calcolare il coseno e la tangente, in maniera tale da poterle utilizzare anche con angoli in gradi;
ex-4
si scriva un programma che calcoli il perimetro dei quadrilateri, contenente tre funzioni sovrapposte: la prima accetti la lunghezza di un solo lato (rombo, quadrato), la seconda di due (rettangolo, parallelogrammo), la terza di quattro (quadrilatero generico);
ex-5
si scriva un numero opportuno di funzioni sovrapposte per calcolare l'elevamento a potenza reale di un numero reale (attenzione: se la base è negativa e l'esponente è non intero, l'operazione non ha significato);


next up previous contents index
Next: Array, enumerati, stringhe Up: Puntatori e riferimenti Previous: Riferimenti   Indice   Indice analitico
Claudio Cicconetti
2000-09-06