Condividi tramite


Overload di funzioni

C++ consente di specificare più funzioni con lo stesso nome nello stesso ambito. Queste funzioni sono chiamate funzioni di overload o overload. Le funzioni di overload consentono di fornire semantiche diverse per una funzione, a seconda dei tipi e del numero degli argomenti.

Si consideri, ad esempio, una print funzione che accetta un std::string argomento. Questa funzione può eseguire attività molto diverse rispetto a una funzione che accetta un argomento di tipo double. L'overload impedisce di dover usare nomi come print_string o print_double. In fase di compilazione, il compilatore sceglie l'overload da usare in base ai tipi e al numero di argomenti passati dal chiamante. Se si chiama print(42.0), la void print(double d) funzione viene richiamata. Se si chiama print("hello world"), viene richiamato l'overload void print(std::string) .

È possibile eseguire l'overload di funzioni membro e funzioni libere. La tabella seguente illustra quali parti di una dichiarazione di funzione C++ usano per distinguere i gruppi di funzioni con lo stesso nome nello stesso ambito.

Considerazioni sull'overload

Elemento dichiarazione di funzione Usato per l'overload?
Tipo restituito di funzione No
Numero di argomenti
Tipo di argomenti
Presenza o assenza di puntini di sospensione
Uso dei typedef nomi No
Limiti di matrice non specificati No
const oppure volatile Sì, se applicato all'intera funzione
Qualificatori di riferimento (& e &&)

Esempio

L'esempio seguente illustra come usare gli overload delle funzioni:

// function_overloading.cpp
// compile with: /EHsc
#include <iostream>
#include <math.h>
#include <string>

// Prototype three print functions.
int print(std::string s);             // Print a string.
int print(double dvalue);            // Print a double.
int print(double dvalue, int prec);  // Print a double with a
                                     //  given precision.
using namespace std;
int main(int argc, char *argv[])
{
    const double d = 893094.2987;
    if (argc < 2)
    {
        // These calls to print invoke print( char *s ).
        print("This program requires one argument.");
        print("The argument specifies the number of");
        print("digits precision for the second number");
        print("printed.");
        exit(0);
    }

    // Invoke print( double dvalue ).
    print(d);

    // Invoke print( double dvalue, int prec ).
    print(d, atoi(argv[1]));
}

// Print a string.
int print(string s)
{
    cout << s << endl;
    return cout.good();
}

// Print a double in default precision.
int print(double dvalue)
{
    cout << dvalue << endl;
    return cout.good();
}

//  Print a double in specified precision.
//  Positive numbers for precision indicate how many digits
//  precision after the decimal point to show. Negative
//  numbers for precision indicate where to round the number
//  to the left of the decimal point.
int print(double dvalue, int prec)
{
    // Use table-lookup for rounding/truncation.
    static const double rgPow10[] = {
        10E-7, 10E-6, 10E-5, 10E-4, 10E-3, 10E-2, 10E-1,
        10E0, 10E1,  10E2,  10E3,  10E4, 10E5,  10E6 };
    const int iPowZero = 6;

    // If precision out of range, just print the number.
    if (prec < -6 || prec > 7)
    {
        return print(dvalue);
    }
    // Scale, truncate, then rescale.
    dvalue = floor(dvalue / rgPow10[iPowZero - prec]) *
        rgPow10[iPowZero - prec];
    cout << dvalue << endl;
    return cout.good();
}

Il codice precedente mostra gli overload della funzione nell'ambito print del file.

L'argomento predefinito non è considerato parte del tipo di funzione. Pertanto, non viene usato per la selezione di funzioni di overload. Due funzioni che differiscono solo nei relativi argomenti predefiniti vengono considerate più definizioni anziché funzioni in overload.

Non è possibile fornire argomenti predefiniti per gli operatori di overload.

Corrispondenza degli argomenti

Il compilatore seleziona la funzione di overload da richiamare in base alla corrispondenza migliore tra le dichiarazioni di funzione nell'ambito corrente per gli argomenti forniti nella chiamata di funzione. Se viene trovata una funzione appropriata, questa viene chiamata. "Adatto" in questo contesto significa:

  • Una corrispondenza esatta è stata trovata.

  • Una conversione semplice è stata eseguita.

  • Una promozione a intero è stata eseguita.

  • Esiste una conversione standard al tipo di argomento desiderato.

  • Esiste una conversione definita dall'utente (un operatore di conversione o un costruttore) nel tipo di argomento desiderato.

  • Sono stati trovati argomenti rappresentati dai puntini di sospensione.

Il compilatore crea un set di funzioni candidate per ciascun argomento. Le funzioni candidate sono funzioni in cui l'argomento effettivo in quella posizione può essere convertito nel tipo dell'argomento formale.

Un set delle "migliori funzioni di corrispondenza" viene compilato per ogni argomento e la funzione selezionata è l'intersezione di tutti i set. Se l'intersezione contiene più funzioni, l'overload è ambiguo e genera un errore. La funzione selezionata alla fine è sempre una corrispondenza migliore rispetto a ogni altra funzione nel gruppo per almeno un argomento. Se non esiste un chiaro vincitore, la chiamata di funzione genera un errore del compilatore.

Considerare le seguenti dichiarazioni (le funzioni vengono contrassegnate come Variant 1, Variant 2 e Variant 3 per l'identificazione nella discussione seguente):

Fraction &Add( Fraction &f, long l );       // Variant 1
Fraction &Add( long l, Fraction &f );       // Variant 2
Fraction &Add( Fraction &f, Fraction &f );  // Variant 3

Fraction F1, F2;

Si consideri la seguente istruzione:

F1 = Add( F2, 23 );

Con l'istruzione precedente vengono compilati due set:

Set 1: Funzioni candidate con primo argomento di tipo Fraction Set 2: Funzioni candidate il cui secondo argomento può essere convertito in tipo int
Variante 1 Variante 1 (int può essere convertita in long usando una conversione standard)
Variante 3

Le funzioni in Set 2 sono funzioni con conversioni implicite dal tipo di parametro effettivo al tipo di parametro formale. Una di queste funzioni ha il più piccolo "costo" per convertire il tipo di parametro effettivo nel tipo di parametro formale corrispondente.

L'intersezione dei due set è la Variante 1. Un esempio di una chiamata di funzione ambigua è:

F1 = Add( 3, 6 );

Con la chiamata di funzione precedente vengono compilati i seguenti set:

Set 1: Funzioni candidate con primo argomento di tipo int Set 2: Funzioni candidate con secondo argomento di tipo int
Variante 2 (int può essere convertita in long usando una conversione standard) Variante 1 (int può essere convertita in long usando una conversione standard)

Poiché l'intersezione di questi due set è vuota, il compilatore genera un messaggio di errore.

Per la corrispondenza degli argomenti, una funzione con n argomenti predefiniti viene considerata come n+1 funzioni separate, ognuna con un numero diverso di argomenti.

I puntini di sospensione (...) fungono da carattere jolly e corrispondono a qualsiasi argomento effettivo. Può causare molti set ambigui, se non si progettano set di funzioni di overload con estrema attenzione.

Nota

L'ambiguità delle funzioni di overload non può essere determinata fino a quando non viene rilevata una chiamata di funzione. A questo punto, i set vengono compilati per ogni argomento nella chiamata di funzione ed è possibile determinare se esiste un overload non ambiguo. Ciò significa che le ambiguità possono rimanere nel codice fino a quando non vengono richiamate da una chiamata di funzione specifica.

Differenze tra tipi di argomenti

Le funzioni in overload fanno una differenza tra i tipi di argomento che accettano inizializzatori diversi. Pertanto, un argomento di tipo specificato e un riferimento al tipo sono considerati uguali allo scopo dell'overload. Sono considerati gli stessi perché accettano gli stessi inizializzatori. Ad esempio, max( double, double ) è considerato uguale a max( double &, double & ). La dichiarazione di tali due funzioni causa un errore.

Per lo stesso motivo, gli argomenti della funzione di un tipo modificato da const o volatile non vengono trattati in modo diverso rispetto al tipo di base ai fini dell'overload.

Tuttavia, il meccanismo di overload delle funzioni può distinguere tra i riferimenti qualificati da const e volatile e i riferimenti al tipo di base. Rende possibile il codice, ad esempio quanto segue:

// argument_type_differences.cpp
// compile with: /EHsc /W3
// C4521 expected
#include <iostream>

using namespace std;
class Over {
public:
   Over() { cout << "Over default constructor\n"; }
   Over( Over &o ) { cout << "Over&\n"; }
   Over( const Over &co ) { cout << "const Over&\n"; }
   Over( volatile Over &vo ) { cout << "volatile Over&\n"; }
};

int main() {
   Over o1;            // Calls default constructor.
   Over o2( o1 );      // Calls Over( Over& ).
   const Over o3;      // Calls default constructor.
   Over o4( o3 );      // Calls Over( const Over& ).
   volatile Over o5;   // Calls default constructor.
   Over o6( o5 );      // Calls Over( volatile Over& ).
}

Output

Over default constructor
Over&
Over default constructor
const Over&
Over default constructor
volatile Over&

Anche i puntatori a const e volatile gli oggetti sono considerati diversi dai puntatori al tipo di base ai fini dell'overload.

Corrispondenza e conversioni di argomenti

Quando il compilatore tenta di far corrispondere gli argomenti effettivi con gli argomenti nelle dichiarazioni di funzione, può fornire le conversioni standard o definite dall'utente per ottenere il tipo corretto se non è disponibile alcuna corrispondenza esatta. L'applicazione delle conversioni è soggetta a queste regole:

  • Le sequenze di conversioni che contengono più conversioni definite dall'utente non vengono considerate.

  • Le sequenze di conversioni che possono essere abbreviate rimuovendo le conversioni intermedie non vengono considerate.

La sequenza risultante delle conversioni, se presente, viene chiamata sequenza di corrispondenza migliore. Esistono diversi modi per convertire un oggetto di tipo in tipo intunsigned long usando conversioni standard (descritte in Conversioni standard):

  • Eseguire la conversione da int a long e quindi da long a unsigned long.

  • Eseguire la conversione da int a unsigned long.

Anche se la prima sequenza raggiunge l'obiettivo desiderato, non è la sequenza di corrispondenza migliore, perché esiste una sequenza più breve.

Nella tabella seguente viene illustrato un gruppo di conversioni denominate conversioni semplici. Le conversioni semplici hanno un effetto limitato sulla sequenza scelta dal compilatore come corrispondenza migliore. L'effetto delle conversioni semplici è descritto dopo la tabella.

Conversioni semplici

Tipo di argomento Tipo convertito
type-name type-name&
type-name& type-name
type-name[] type-name*
type-name(argument-list) (*type-name)(argument-list)
type-name const type-name
type-name volatile type-name
type-name* const type-name*
type-name* volatile type-name*

La sequenza in cui vengono tentate le conversioni è la seguente:

  1. Corrispondenza esatta. Una corrispondenza esatta tra i tipi con cui viene chiamata la funzione e i tipi dichiarati nel prototipo di funzione è sempre la corrispondenza ottimale. Le sequenze di conversioni semplici vengono classificate come corrispondenze esatte. Tuttavia, le sequenze che non effettuano alcuna di queste conversioni sono considerate migliori delle sequenze che convertono:

    • Dal puntatore al puntatore a const (type-name* a const type-name*).

    • Dal puntatore al puntatore a volatile (type-name* a volatile type-name*).

    • Da riferimento, a riferimento a (type-name& a const type-name&const ).

    • Da riferimento, a riferimento a (type-name& a volatile type&volatile ).

  2. Corrispondenza mediante le promozioni. Qualsiasi sequenza non classificata come corrispondenza esatta che contiene solo promozioni integrali, conversioni da float a doublee conversioni semplici viene classificata come corrispondenza utilizzando promozioni. Sebbene non sia ottimale come una corrispondenza esatta, l'utilizzo delle promozioni è preferibile rispetto a una corrispondenza che utilizza le conversioni standard.

  3. Corrispondenza che usa le conversioni standard. Qualsiasi sequenza non classificata come una corrispondenza esatta o una corrispondenza che usa promozioni solo con conversioni standard e semplici viene classificata come corrispondenza che usa le conversioni standard. All'interno di questa categoria, sono necessarie le seguenti regole:

    • La conversione da un puntatore a una classe derivata, in un puntatore a una classe base diretta o indiretta è preferibile convertire in void * o const void *.

    • La conversione da un puntatore a una classe derivata, a un puntatore a una classe base produce una corrispondenza migliore quanto più la classe base è vicina a una classe base diretta. Si supponga che la gerarchia di classi sia come illustrato nella figura seguente:

Example class hierarchy showing that class A inherits from B which inherits from C which inherits from D.
Grafico che mostra le conversioni preferite.

La conversione dal tipo D* al tipo C* è preferibile rispetto alla conversione dal tipo D* al tipo B*. In modo simile, la conversione dal tipo D* al tipo B* è preferibile rispetto alla conversione dal tipo D* al tipo A*.

La stessa regola si applica alle conversioni di riferimento. La conversione dal tipo D& al tipo C& è preferibile rispetto alla conversione dal tipo D& al tipo B& e così via.

La stessa regola si applica alle conversioni da puntatore a membro. La conversione dal tipo T D::* al tipo T C::* è preferibile rispetto alla conversione dal tipo T D::* al tipo T B::* e così via (dove T è il tipo del membro).

La regola precedente è valida solo insieme a un determinato percorso di derivazione. Esaminare il grafico illustrato nella seguente figura.

Diagram of multiple inheritance that shows preferred conversions. Class C is the base class of class B and D. Class A inherits from class B
Grafico a ereditarietà multipla che mostra le conversioni preferite.

La conversione dal tipo C* al tipo B* è preferibile rispetto alla conversione dal tipo C* al tipo A*. Il motivo è che si trovano nello stesso percorso e B* è più vicino. Tuttavia, la conversione dal tipo al tipo C*D* non è preferibile alla conversione al tipo A*. Non esiste alcuna preferenza perché le conversioni seguono percorsi diversi.

  1. Corrispondenza con le conversioni definite dall'utente. Questa sequenza non può essere classificata come corrispondenza esatta, una corrispondenza usando promozioni o una corrispondenza usando conversioni standard. Per essere classificato come corrispondenza con conversioni definite dall'utente, la sequenza deve contenere solo conversioni definite dall'utente, conversioni standard o conversioni semplici. Una corrispondenza con le conversioni definite dall'utente è considerata una corrispondenza migliore rispetto a una corrispondenza con i puntini di sospensione (...) ma non come una corrispondenza valida come corrispondenza con le conversioni standard.

  2. Corrispondenza con i puntini di sospensione. Qualsiasi sequenza che corrisponde ai puntini di sospensione nella dichiarazione viene classificata come corrispondenza con i puntini di sospensione. È considerato la corrispondenza più debole.

Le conversioni definite dall'utente vengono applicate se non esiste alcuna conversione o promozione integrata. Queste conversioni vengono selezionate in base al tipo di argomento corrispondente. Osservare il codice seguente:

// argument_matching1.cpp
class UDC
{
public:
   operator int()
   {
      return 0;
   }
   operator long();
};

void Print( int i )
{
};

UDC udc;

int main()
{
   Print( udc );
}

Le conversioni definite dall'utente disponibili per la classe UDC provengono dal tipo int e dal tipo long. Di conseguenza, il compilatore considera le conversioni per il tipo di oggetto di cui si sta eseguendo la corrispondenza: UDC. Una conversione in int esiste ed è selezionata.

Durante il processo di corrispondenza degli argomenti, è possibile applicare le conversioni standard sia agli argomenti che al risultato di una conversione definita dall'utente. Pertanto, funziona il codice seguente:

void LogToFile( long l );
...
UDC udc;
LogToFile( udc );

In questo esempio il compilatore richiama una conversione definita dall'utente, operator long, per convertire udc in tipo long. Se non è stata definita alcuna conversione definita dall'utente nel tipo long , il compilatore convertirà prima il tipo in tipo UDCint usando la conversione definita dall'utente operator int . Applica quindi la conversione standard dal tipo int al tipo long in modo che corrisponda all'argomento nella dichiarazione.

Se sono necessarie conversioni definite dall'utente per trovare una corrispondenza con un argomento, le conversioni standard non vengono usate durante la valutazione della corrispondenza migliore. Anche se più di una funzione candidata richiede una conversione definita dall'utente, le funzioni vengono considerate uguali. Ad esempio:

// argument_matching2.cpp
// C2668 expected
class UDC1
{
public:
   UDC1( int );  // User-defined conversion from int.
};

class UDC2
{
public:
   UDC2( long ); // User-defined conversion from long.
};

void Func( UDC1 );
void Func( UDC2 );

int main()
{
   Func( 1 );
}

Entrambe le versioni di Func richiedono una conversione definita dall'utente per convertire il tipo int nell'argomento del tipo di classe. Le conversioni possibili sono:

  • Eseguire la conversione dal tipo al tipo intUDC1 (conversione definita dall'utente).

  • Eseguire la conversione dal tipo al tipo intlong, quindi eseguire la conversione in tipo UDC2 (conversione in due passaggi).

Anche se il secondo richiede sia una conversione standard che la conversione definita dall'utente, le due conversioni vengono comunque considerate uguali.

Nota

Le conversioni definite dall'utente vengono considerate conversioni per costruzione o conversione da inizializzazione. Il compilatore considera entrambi i metodi uguali quando determina la corrispondenza migliore.

Corrispondenza degli argomenti e puntatore this

Le funzioni membro della classe vengono trattate in modo diverso, a seconda che siano dichiarate come static. static Le funzioni non hanno un argomento implicito che fornisce il this puntatore, quindi vengono considerate avere un argomento minore rispetto alle normali funzioni membro. In caso contrario, vengono dichiarati in modo identico.

Le funzioni membro che non static richiedono che il puntatore implicito this corrisponda al tipo di oggetto chiamato dalla funzione. In alternativa, per gli operatori di overload, è necessario che il primo argomento corrisponda all'oggetto a cui viene applicato l'operatore. Per altre informazioni sugli operatori di overload, vedere Operatori di overload.

A differenza di altri argomenti nelle funzioni di overload, il compilatore non introduce oggetti temporanei e non tenta alcuna conversione quando si tenta di trovare la corrispondenza con l'argomento del this puntatore.

Quando l'operatore -> member-selection viene usato per accedere a una funzione membro della classe class_name, l'argomento this puntatore ha un tipo di class_name * const. Se i membri vengono dichiarati come const o volatile, i tipi sono const class_name * const rispettivamente e volatile class_name * const.

L'operatore di selezione dei membri . funziona esattamente nello stesso modo, ad eccezione che un operatore & (address-of) implicito sia anteposto al nome di oggetto. L'esempio seguente illustra tale funzionamento:

// Expression encountered in code
obj.name

// How the compiler treats it
(&obj)->name

L'operando sinistro degli operatori ->* e .* (puntatore a membro) vengono considerati nello stesso modo degli operatori . e -> (selezione dei membri) in relazione alla corrispondenza dell'argomento.

Qualificatori di riferimento sulle funzioni membro

I qualificatori di riferimento consentono di eseguire l'overload di una funzione membro in base al fatto che l'oggetto a cui this punta sia un rvalue o un lvalue. Usare questa funzionalità per evitare operazioni di copia non necessarie negli scenari in cui si sceglie di non fornire l'accesso dei puntatori ai dati. Si supponga, ad esempio, che la classe C inizializzi alcuni dati nel relativo costruttore e restituisca una copia di tali dati nella funzione get_data()membro . Se un oggetto di tipo C è un rvalue che sta per essere eliminato definitivamente, il compilatore sceglie l'overload get_data() && , che si sposta invece di copiare i dati.

#include <iostream>
#include <vector>

using namespace std;

class C
{
public:
    C() {/*expensive initialization*/}
    vector<unsigned> get_data() &
    {
        cout << "lvalue\n";
        return _data;
    }
    vector<unsigned> get_data() &&
    {
        cout << "rvalue\n";
        return std::move(_data);
    }

private:
    vector<unsigned> _data;
};

int main()
{
    C c;
    auto v = c.get_data(); // get a copy. prints "lvalue".
    auto v2 = C().get_data(); // get the original. prints "rvalue"
    return 0;
}

Restrizioni relative all'overload

Un set di funzioni in overload accettabile è regolato da numerose restrizioni:

  • È necessario che due funzioni presenti in un set di funzioni in overload abbiano elenchi di argomenti differenti.

  • L'overload di funzioni con elenchi di argomenti degli stessi tipi, in base al solo tipo restituito, è un errore.

    Sezione specifica Microsoft

    È possibile eseguire l'overload operator new in base al tipo restituito, in particolare, in base al modificatore del modello di memoria specificato.

    Fine sezione specifica Microsoft

  • Le funzioni membro non possono essere sottoposte a overload solo perché una è static e l'altra non staticè .

  • typedef le dichiarazioni non definiscono nuovi tipi; introducono sinonimi per i tipi esistenti. Non influiscono sul meccanismo di overload. Osservare il codice seguente:

    typedef char * PSTR;
    
    void Print( char *szToPrint );
    void Print( PSTR szToPrint );
    

    Le due funzioni precedenti hanno elenchi di argomenti identici. PSTR è un sinonimo di tipo char *. Nell'ambito del membro, questo codice genera un errore.

  • I tipi enumerati sono tipi distinti e possono essere utilizzati per distinguere le funzioni in overload.

  • I tipi "matrice di" e "puntatore a" sono considerati identici ai fini della distinzione tra le funzioni di overload, ma solo per le matrici unidimensionali. Queste funzioni di overload sono in conflitto e generano un messaggio di errore:

    void Print( char *szToPrint );
    void Print( char szToPrint[] );
    

    Per le matrici di dimensioni superiori, le seconde e successive dimensioni vengono considerate parte del tipo. Vengono usati per distinguere tra le funzioni di overload:

    void Print( char szToPrint[] );
    void Print( char szToPrint[][7] );
    void Print( char szToPrint[][9][42] );
    

Overload, override e nascondere

Qualsiasi dichiarazione di funzione con lo stesso nome nello stesso ambito può fare riferimento alla stessa funzione o a due funzioni di overload discrete. Se gli elenchi di argomenti delle dichiarazioni contengono argomenti di tipi equivalenti (come descritto nella sezione precedente), le dichiarazioni di funzione si riferiscono alla stessa funzione. In caso contrario, si riferiscono a due diverse funzioni selezionate utilizzando l'overload.

L'ambito della classe è rigorosamente osservato. Una funzione dichiarata in una classe base non si trova nello stesso ambito di una funzione dichiarata in una classe derivata. Se una funzione in una classe derivata viene dichiarata con lo stesso nome di una virtual funzione nella classe base, la funzione della classe derivata esegue l'override della funzione della classe base. Per altre informazioni, vedere Funzioni virtuali.

Se la funzione della classe di base non viene dichiarata come virtual, la funzione di classe derivata viene detta nascosta . Sia l'override che il nascondimento sono distinti dall'overload.

L'ambito del blocco viene osservato rigorosamente. Una funzione dichiarata nell'ambito del file non si trova nello stesso ambito di una funzione dichiarata in locale. Se una funzione dichiarata localmente ha lo stesso nome di una funzione dichiarata in ambito di file, la funzione dichiarata localmente nasconde la funzione con ambito di file anziché causare l'overload. Ad esempio:

// declaration_matching1.cpp
// compile with: /EHsc
#include <iostream>

using namespace std;
void func( int i )
{
    cout << "Called file-scoped func : " << i << endl;
}

void func( char *sz )
{
    cout << "Called locally declared func : " << sz << endl;
}

int main()
{
    // Declare func local to main.
    extern void func( char *sz );

    func( 3 );   // C2664 Error. func( int ) is hidden.
    func( "s" );
}

Il codice precedente mostra due definizioni dalla funzione func. La definizione che accetta un argomento di tipo char * è locale a main causa dell'istruzione extern . Pertanto, la definizione che accetta un argomento di tipo int è nascosta e la prima chiamata a func è in errore.

Per funzioni membro in overload, a versioni diverse della funzione possono essere assegnati privilegi di accesso differenti. Sono ancora considerati nell'ambito della classe di inclusione e quindi sono funzioni di overload. Si consideri il codice seguente, in cui la funzione membro Deposit è in overload; una versione è pubblica, l'altra privata.

Lo scopo di questo esempio è quello di fornire una classe Account in cui è richiesta una password corretta per eseguire i depositi. Questa operazione viene eseguita usando l'overload.

La chiamata a Deposit in Account::Deposit chiama la funzione membro privato. Questa chiamata è corretta perché Account::Deposit è una funzione membro e ha accesso ai membri privati della classe .

// declaration_matching2.cpp
class Account
{
public:
   Account()
   {
   }
   double Deposit( double dAmount, char *szPassword );

private:
   double Deposit( double dAmount )
   {
      return 0.0;
   }
   int Validate( char *szPassword )
   {
      return 0;
   }

};

int main()
{
    // Allocate a new object of type Account.
    Account *pAcct = new Account;

    // Deposit $57.22. Error: calls a private function.
    // pAcct->Deposit( 57.22 );

    // Deposit $57.22 and supply a password. OK: calls a
    //  public function.
    pAcct->Deposit( 52.77, "pswd" );
}

double Account::Deposit( double dAmount, char *szPassword )
{
   if ( Validate( szPassword ) )
      return Deposit( dAmount );
   else
      return 0.0;
}

Vedi anche

Funzioni (C++)