Sovraccarico delle funzioni - Function overloading

In alcuni linguaggi di programmazione , l' overload di funzioni o l' overload di metodi è la capacità di creare più funzioni con lo stesso nome con implementazioni diverse. Le chiamate a una funzione di overload eseguiranno un'implementazione specifica di tale funzione appropriata al contesto della chiamata, consentendo a una chiamata di funzione di eseguire attività diverse a seconda del contesto.

Ad esempio, doTask()e doTask(object o)sono funzioni sovraccaricate. Per chiamare quest'ultimo, un oggetto deve essere passato come parametro , mentre il primo non richiede un parametro e viene chiamato con un campo parametro vuoto. Un errore comune sarebbe quello di assegnare un valore predefinito all'oggetto nella seconda funzione, che comporterebbe un errore di chiamata ambiguo , poiché il compilatore non saprebbe quale dei due metodi utilizzare.

Un altro esempio è una Print(object o)funzione che esegue azioni diverse a seconda che si tratti di stampare testo o foto. Le due diverse funzioni possono essere sovraccaricate come Print(text_object T); Print(image_object P). Se scriviamo le funzioni di stampa sovraccaricate per tutti gli oggetti che il nostro programma "stampa", non dobbiamo mai preoccuparci del tipo dell'oggetto, e la chiamata alla funzione corretta di nuovo, la chiamata è sempre: Print(something).

Lingue che supportano il sovraccarico

Le lingue che supportano l'overload delle funzioni includono, ma non sono necessariamente limitate a, le seguenti:

Regole nel sovraccarico delle funzioni

  • Lo stesso nome di funzione viene utilizzato per più di una definizione di funzione
  • Le funzioni devono differire per l' arità o per i tipi dei loro parametri

È una classificazione del polimorfismo statico in cui una chiamata di funzione viene risolta utilizzando un algoritmo di "miglior corrispondenza", in cui la particolare funzione da chiamare viene risolta trovando la migliore corrispondenza dei tipi di parametro formali con i tipi di parametro effettivi. I dettagli di questo algoritmo variano da lingua a lingua.

L'overload delle funzioni è in genere associato a linguaggi di programmazione tipizzati in modo statico che impongono il controllo del tipo nelle chiamate di funzione . Una funzione sovraccaricata è in realtà solo un insieme di funzioni diverse che hanno lo stesso nome. La determinazione della funzione da utilizzare per una particolare chiamata viene risolta in fase di compilazione .

In Java , l'overload delle funzioni è noto anche come polimorfismo in fase di compilazione e polimorfismo statico.

L'overload di funzioni non deve essere confuso con forme di polimorfismo in cui la scelta viene effettuata in fase di esecuzione, ad esempio tramite funzioni virtuali , anziché staticamente.

Esempio: sovraccarico di funzioni in C++

#include <iostream>

int Volume(int s) {  // Volume of a cube.
  return s * s * s;
}

double Volume(double r, int h) {  // Volume of a cylinder.
  return 3.1415926 * r * r * static_cast<double>(h);
}

long Volume(long l, int b, int h) {  // Volume of a cuboid.
  return l * b * h;
}

int main() {
  std::cout << Volume(10);
  std::cout << Volume(2.5, 8);
  std::cout << Volume(100l, 75, 15);
}

Nell'esempio sopra, il volume di ciascun componente viene calcolato utilizzando una delle tre funzioni denominate "volume", con selezione basata sul diverso numero e tipo di parametri effettivi.

Sovraccarico del costruttore

I costruttori , usati per creare istanze di un oggetto, possono anche essere sovraccaricati in alcuni linguaggi di programmazione orientati agli oggetti . Poiché in molte lingue il nome del costruttore è predeterminato dal nome della classe, sembrerebbe che possa esserci un solo costruttore. Ogni volta che sono necessari più costruttori, devono essere implementati come funzioni di overload. In C++ , i costruttori predefiniti non accettano parametri, istanziando i membri dell'oggetto con i valori predefiniti appropriati. Ad esempio, un costruttore predefinito per un oggetto conto ristorante scritto in C++ potrebbe impostare la mancia al 15%:

Bill()
    : tip(0.15), // percentage
      total(0.0)
{ }

Lo svantaggio è che sono necessari due passaggi per modificare il valore dell'oggetto Bill creato. Quanto segue mostra la creazione e la modifica dei valori all'interno del programma principale:

Bill cafe;
cafe.tip = 0.10;
cafe.total = 4.00;

Sovraccaricando il costruttore, è possibile passare tip e total come parametri alla creazione. Questo mostra il costruttore in overload con due parametri. Questo costruttore sovraccarico viene inserito nella classe così come il costruttore originale che abbiamo usato prima. Quale viene utilizzato dipende dal numero di parametri forniti quando viene creato il nuovo oggetto Bill (nessuno o due):

Bill(double tip, double total)
    : tip(tip),
      total(total)
{ }

Ora una funzione che crea un nuovo oggetto Bill può passare due valori nel costruttore e impostare i membri dati in un unico passaggio. Quanto segue mostra la creazione e l'impostazione dei valori:

Bill cafe(0.10, 4.00);

Questo può essere utile per aumentare l'efficienza del programma e ridurre la lunghezza del codice.

Un altro motivo per l'overload del costruttore può essere l'applicazione di membri dati obbligatori. In questo caso il costruttore predefinito viene dichiarato privato o protetto (o preferibilmente eliminato dal C++11 ) per renderlo inaccessibile dall'esterno. Per il conto sopra il totale potrebbe essere l'unico parametro del costruttore, poiché un conto non ha un valore predefinito ragionevole per il totale, mentre il valore predefinito di tip è 0,15.

complicazioni

Due problemi interagiscono e complicano l'overload delle funzioni: il mascheramento del nome (a causa dell'ambito ) e la conversione del tipo implicita .

Se una funzione viene dichiarata in uno scope, e poi un'altra funzione con lo stesso nome viene dichiarata in uno scope interno, ci sono due naturali possibili comportamenti di sovraccarico: la dichiarazione interna maschera la dichiarazione esterna (indipendentemente dalla firma), o entrambi la dichiarazione interna e la dichiarazione esterna sono entrambe incluse nell'overload, con la dichiarazione interna che maschera la dichiarazione esterna solo se la firma corrisponde. Il primo è preso in C++: "in C++, non c'è sovraccarico tra gli ambiti". Di conseguenza, per ottenere un set di overload con funzioni dichiarate in ambiti diversi, è necessario importare in modo esplicito le funzioni dall'ambito esterno nell'ambito interno, con la usingparola chiave.

La conversione di tipo implicita complica l'overload della funzione perché se i tipi di parametri non corrispondono esattamente alla firma di una delle funzioni di overload, ma possono corrispondere dopo la conversione del tipo, la risoluzione dipende dalla conversione del tipo scelta.

Questi possono combinarsi in modi confusi: una corrispondenza inesatta dichiarata in un ambito interno può mascherare una corrispondenza esatta dichiarata in un ambito esterno, per esempio.

Ad esempio, per avere una classe derivata con una funzione sovraccaricata che prende a doubleo an int, usando la funzione che prende un intdalla classe base, in C++, si scriverebbe:

class B {
 public:
  void F(int i);
};

class D : public B {
 public:
  using B::F;
  void F(double d);
};

Non riuscendo a includere i usingrisultati in un intparametro passato a Fnella classe derivata che viene convertito in un doppio e che corrisponde alla funzione nella classe derivata, piuttosto che nella classe base; L'inclusione usingdetermina un overload nella classe derivata e quindi la corrispondenza con la funzione nella classe base.

Avvertenze

Se un metodo è progettato con un numero eccessivo di overload, potrebbe essere difficile per gli sviluppatori discernere quale overload viene chiamato semplicemente leggendo il codice. Ciò è particolarmente vero se alcuni dei parametri sovraccaricati sono di tipi ereditati da altri possibili parametri (ad esempio "oggetto"). Un IDE può eseguire la risoluzione del sovraccarico e visualizzare (o navigare) il sovraccarico corretto.

L'overload basato sul tipo può anche ostacolare la manutenzione del codice, in cui gli aggiornamenti del codice possono modificare accidentalmente il metodo di overload scelto dal compilatore.

Guarda anche

Riferimenti

link esterno