Funktionsüberladung

C++ lässt die Angabe mehrerer Funktionen mit dem gleichen Namen im gleichen Gültigkeitsbereich zu. Diese Funktionen werden als überladene Funktionen bezeichnet. Mit überladenen Funktionen können Sie je nach Typ und Anzahl von Argumenten unterschiedliche Semantik für eine Funktion bereitstellen.

Beispielsweise kann eine print Funktion, die ein std::string Argument annimmt, sehr andere Aufgaben ausführen als eine Funktion, die ein Argument vom Typ annimmt. double Das Überladen verhindert, dass Sie Namen wie oder verwenden print_stringprint_double müssen. Zur Kompilierzeit wählt der Compiler basierend auf dem Typ der vom Aufrufer übergebenen Argumente aus, welche Überladung verwendet werden soll. Wenn Sie print(42.0) aufrufen, wird die void print(double d) Funktion aufgerufen. Wenn Sie print("hello world") aufrufen, wird die void print(std::string) Überladung aufgerufen.

Sie können sowohl Memberfunktionen als auch Nichtmemberfunktionen überladen. Die folgende Tabelle zeigt, welche Teile einer Funktionsdeklaration von C++ verwendet werden, um zwischen Gruppen von Funktionen mit dem gleichen Namen und dem gleichen Gültigkeitsbereich zu differenzieren.

Überlegungen zur Überladung

Funktionsdeklarationselement Wird zum Überladen verwendet?
Funktionsrückgabetyp Nein
Anzahl der Argumente Yes
Typ der Argumente Yes
Vorhandensein oder Abwesenheit von Auslassungszeichen Yes
Verwenden von typedef Namen Nein
Nicht angegebene Arraygrenzen Nein
const oder volatile Ja, wenn sie auf die gesamte Funktion angewendet wird
Ref-Qualifizierer Ja

Beispiel

Das folgende Beispiel veranschaulicht, wie das Überladen verwendet werden kann.

// 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();
}

Der vorhergehende Code zeigt das Überladen der print-Funktion im Dateigültigkeitsbereich an.

Das Standardargument wird nicht als Teil des Funktionstyps betrachtet. Daher wird sie nicht zum Auswählen überladener Funktionen verwendet. Zwei Funktionen, die sich nur bei den Standardargumenten unterscheiden, werden als mehrfache Definitionen und nicht als überladene Funktionen betrachtet.

Standardargumente können nicht für überladene Operatoren angegeben werden.

Argumentübereinstimmung

Überladene Funktionen werden für die beste Übereinstimmung von Funktionsdeklarationen im aktuellen Bereich für Argumente ausgewählt, die im Funktionsaufruf angegeben werden. Wenn eine passende Funktion gefunden wird, wird diese Funktion aufgerufen. "Geeignet" bedeutet in diesem Kontext:

  • Ein exakte Übereinstimmung wurde gefunden.

  • Eine triviale Konvertierung wurde ausgeführt.

  • Eine ganzzahlige Heraufstufung wurde ausgeführt.

  • Eine Standardkonvertierung zum gewünschten Argumenttyp ist vorhanden.

  • Eine benutzerdefinierte Konvertierung (entweder Konvertierungsoperator oder Konstruktor) auf den gewünschten Argumenttyp ist vorhanden.

  • Es wurden durch Ellipsen dargestellte Argumente gefunden.

Der Compiler erstellt einen Satz Kandidatenfunktionen für jedes Argument. Kandidatenfunktionen sind Funktionen, in denen das tatsächliche Argument an dieser Position in den Typ des formalen Arguments konvertiert werden kann.

Ein Satz von „am besten passenden Funktionen“ wird für jedes Argument erstellt, und die ausgewählte Funktion ist die Schnittmenge aller Sätze. Wenn die Schnittmenge mehr als eine Funktion enthält, ist das Überladen mehrdeutig und generiert einen Fehler. Die letztendlich ausgewählte Funktion stimmt stets besser überein als jede andere Funktion in der Gruppe für mindestens ein Argument. Wenn es keinen eindeutigen Gewinner gibt, generiert der Funktionsaufruf einen Fehler.

Berücksichtigen Sie die folgenden Deklarationen (Funktionen sind zur Identifikation der folgenden Erläuterung als Variant 1, Variant 2 und Variant 3 gekennzeichnet):

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;

Betrachten Sie folgende Anweisung:

F1 = Add( F2, 23 );

Die vorhergehende Anweisung erstellt zwei Sätze:

Satz 1: Kandidatenfunktionen, deren erstes Argument vom Typ „fraction“ ist Set 2: Kandidatenfunktionen, deren zweites Argument in den Typ konvertiert werden kann int
Variant 1 Variante 1 ( int kann long mithilfe einer Standardkonvertierung in konvertiert werden)
Variant 3

Funktionen in Set 2 sind Funktionen, für die implizite Konvertierungen vom tatsächlichen Parametertyp in den formalen Parametertyp vorhanden sind. Unter diesen Funktionen gibt es eine Funktion, für die die "Kosten" für die Konvertierung des tatsächlichen Parametertyps in den formalen Parametertyp am kleinsten sind.

Die Schnittmenge dieser beiden Sätze ist "Variant 1". Ein Beispiel für einen mehrdeutigen Funktionsaufruf ist:

F1 = Add( 3, 6 );

Der vorhergehende Funktionsaufruf erstellt die folgenden Sätze:

Set 1: Candidate Functions That Have First Argument of Type int Set 2: Candidate Functions That Have Second Argument of Type int
Variante 2 ( int kann long mithilfe einer Standardkonvertierung in konvertiert werden) Variante 1 ( int kann long mithilfe einer Standardkonvertierung in konvertiert werden)

Da die Schnittmenge dieser beiden Mengen leer ist, generiert der Compiler eine Fehlermeldung.

Für den Argumentabgleich wird eine Funktion mit n Standardargumenten als n+1 separate Funktionen behandelt, die jeweils eine andere Anzahl von Argumenten haben.

Die Ellipse (...) dient als Platzhalter; sie stimmt mit jedem tatsächlichen Argument überein. Dies kann zu vielen mehrdeutigen Sätzen führen, wenn Sie Ihre überladenen Funktionssätze nicht mit äußerster Sorgfalt entwerfen.

Hinweis

Mehrdeutigkeit überladener Funktionen kann erst bestimmt werden, wenn ein Funktionsaufruf gefunden wird. An diesem Punkt werden die Sätze für jedes Argument im Funktionsaufruf erstellt, und Sie können bestimmen, ob eine eindeutige Überladung vorhanden ist. Dies bedeutet, dass Mehrdeutigkeiten im Code vorhanden sein dürfen, bis sie durch einen bestimmten Funktionsaufruf eindeutig deklariert werden.

Unterschiede bei Argumenttypen

Überladene Funktionen unterscheiden zwischen Argumenttypen, die verschiedene Initialisierer erfordern. Daher gelten ein Argument eines angegebenen Typs und ein Verweis auf diesen Typ für den Zweck des Überladens als identisch. Sie gelten als identisch, weil sie die gleichen Initialisierer erfordern. max( double, double ) wird beispielsweise identisch mit max( double &, double & ) betrachtet. Das Deklarieren von zwei solcher Funktionen verursacht einen Fehler.

Aus dem gleichen Grund werden Funktionsargumente eines Typs, der durch oder geändert wurde, constvolatile zum Überladen nicht anders behandelt als der Basistyp.

Der Funktionsüberladungsmechanismus kann jedoch zwischen Verweisen, die durch und gekennzeichnet sind, const und Verweisen auf den volatile Basistyp unterscheiden. Code wie der folgende ist möglich:

// 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&

Zeiger auf - und -Objekte werden auch als andere Zeiger const auf den volatile Basistyp zum Überladen betrachtet.

Argumentübereinstimmung und Konvertierungen

Wenn der Compiler versucht, die tatsächlichen Argumente mit den Argumenten in Funktionsdeklarationen abzugleichen, kann er Standardkonvertierungen oder benutzerdefinierte Konvertierungen bereitstellen, um den korrekten Typ zu erhalten, wenn keine genaue Übereinstimmung gefunden wird. Die Anwendung von Konvertierungen unterliegt diesen Regeln:

  • Sequenzen von Konvertierungen, die mehr als eine benutzerdefinierte Konvertierung enthalten, werden nicht berücksichtigt.

  • Sequenzen von Konvertierungen, die gekürzt werden können, indem Konvertierungen im Zwischenformat entfernt werden, werden nicht berücksichtigt.

Das Ergebnis der Konvertierungssequenz, falls vorhanden, ist die am besten passende Sequenz. Es gibt mehrere Möglichkeiten, ein Objekt vom Typ int mithilfe von Standardkonvertierungen in einen Typ zu konvertieren unsigned long (beschrieben unter int

  • Konvertieren Sie von int in und dann von in longlongunsigned long .

  • Konvertieren von int in unsigned long .

Die erste Sequenz, obwohl sie das gewünschte Ziel erreicht, ist nicht die beste übereinstimmende Sequenz, — eine kürzere Sequenz ist vorhanden.

Die folgende Tabelle zeigt eine Gruppe von Konvertierungen, die als triviale Konvertierungen bezeichnet werden, die eine begrenzte Auswirkung auf die Bestimmung haben, welche Sequenz die größte Übereinstimmung hat. Die Instanzen, in denen triviale Konvertierungen sich auf die Sequenzwahl auswirken, werden in der Liste nach der Tabelle behandelt.

Triviale Konvertierungen

Konvertieren von Typ Konvertiert in Typ
type-name type-name
type-name type-name
type-name[ ] type-name
type-name(argument-list) (Typname) (Argumentliste)
type-name constconst
type-name volatilevolatile
type-name constconst*
type-name volatilevolatile*

Die Reihenfolge für Konvertierungen lautet wie folgt:

  1. Genaue Übereinstimmung. Ein genaue Übereinstimmung zwischen den Typen, mit denen die Funktion aufgerufen wird und den Typen, die im Funktionsprototyp deklariert werden, ist immer die beste Übereinstimmung. Sequenzen von trivialen Konvertierungen werden als exakte Übereinstimmungen klassifiziert. Sequenzen, die keine dieser Konvertierungen machen, werden jedoch als besser angesehen als Sequenzen, die konvertieren:

    • Vom Zeiger zum Zeiger auf const ( type* auf consttype* ).

    • Vom Zeiger zum Zeiger auf volatile ( type* auf volatiletype* ).

    • Vom Verweis bis zum Verweis auf const ( type& bis consttype& ).

    • Vom Verweis bis zum Verweis auf volatile ( type& bis volatiletype& ).

  2. Übereinstimmung mithilfe von Erweiterungen. Jede Sequenz, die nicht als genaue Übereinstimmung klassifiziert ist und nur integrale Aufaktionen, Konvertierungen von in und triviale Konvertierungen enthält, wird mithilfe von -Aktionen als Übereinstimmung floatdouble klassifiziert. Obwohl eine Übereinstimmung mit Erweiterungen besser ist als eine Übereinstimmung, die Standardkonvertierungen verwendet, ist sie nicht so gut wie eine genaue Übereinstimmung.

  3. Übereinstimmung mithilfe von Standardkonvertierungen. Jede beliebige Sequenz, die nicht als genaue Übereinstimmung klassifiziert wird, oder eine Übereinstimmung mithilfe von Erweiterungen, die nur Standardkonvertierungen und triviale Konvertierungen enthält, wird als Übereinstimmung mithilfe von Standardkonvertierungen klassifiziert. In dieser Kategorie gelten die folgenden Regeln:

    • Die Konvertierung von einem Zeiger in eine abgeleitete Klasse in einen Zeiger auf eine direkte oder indirekte Basisklasse ist der Konvertierung in oder void *const void * vorzuziehen.

    • Die Konvertierung von einem Zeiger auf eine abgeleitete Klasse in einen Zeiger auf eine Basisklasse erzeugt eine bessere Übereinstimmung, je näher die Basisklasse der direkten Basisklasse ist. Angenommen, die Klassenhierarchie folgt der in der folgenden Abbildung dargestellten Klassenhierarchie.

Graph of preferred conversions.
Graph mit bevorzugten Konvertierungen

Die Konvertierung von Typ D* in Typ C* ist der Konvertierung von Typ D* in Typ B* vorzuziehen. Entsprechend ist die Konvertierung von Typ D* in Typ B* der Konvertierung von Typ D* in Typ A* vorzuziehen.

Die gleiche Regel gilt für Verweiskonvertierungen. Die Konvertierung von Typ D& in Typ C& ist der Konvertierung von Typ D& in Typ B& usw. vorzuziehen.

Die gleiche Regel gilt für pointer-to-member-Konvertierungen. Die Konvertierung von Typ T D::* in Typ T C::* ist der Konvertierung von Typ T D::* in Typ T B::* und so weiter vorzuziehen (wobei T der Typ des Members ist).

Die vorhergehende Regel gilt nur für einen bestimmten Ableitungspfad. Betrachten Sie das Diagramm, das in der folgenden Abbildung dargestellt wird.

Diagram of multiple inheritance that shows preferred conversions.
Diagramm mit mehrfacher Vererbung, das bevorzugte Konvertierungen zeigt

Die Konvertierung von Typ C* in Typ B* ist der Konvertierung von Typ C* in Typ A* vorzuziehen. Der Grund liegt darin, dass sie sich auf dem gleichen Pfad befinden, und B* näher liegt. Die Konvertierung von Typ zu Typ ist der Konvertierung in den Typ jedoch nicht vorzuziehen. Es gibt keine Einstellung, da die Konvertierungen C*D*A* unterschiedlichen Pfaden folgen.

  1. Übereinstimmung mit benutzerdefinierten Konvertierungen. Diese Sequenz kann nicht als genaue Übereinstimmung, Übereinstimmung mithilfe von Aufaktionen oder Übereinstimmung mit Standardkonvertierungen klassifiziert werden. Die Reihenfolge darf nur benutzerdefinierte Konvertierungen, Standardkonvertierungen oder triviale Konvertierungen enthalten, um die Übereinstimmung mit benutzerdefinierten Konvertierungen zu erreichen. Eine Übereinstimmung mit benutzerdefinierten Konvertierungen gilt als eine bessere Übereinstimmung als eine Übereinstimmung mit einem Auslassungszeichen, jedoch als nicht so gut wie eine Übereinstimmung mit Standardkonvertierungen.

  2. Übereinstimmung mit einem Auslassungszeichen. Jede beliebige Sequenz, die Auslassungszeichen in der Deklaration entspricht, wird als Übereinstimmung mit einem Auslassungszeichen klassifiziert. Dies gilt als die schwache Übereinstimmung.

Benutzerdefinierte Konvertierungen werden angewendet, wenn keine integrierte Erweiterung oder Konvertierung vorhanden ist. Diese Konvertierungen werden auf der Grundlage des Typs des Arguments ausgewählt, das abgeglichen wird. Betrachten Sie folgenden Code:

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

void Print( int i )
{
};

UDC udc;

int main()
{
   Print( udc );
}

Die verfügbaren benutzerdefinierten Konvertierungen für die -Klasse UDC sind vom Typ und vom Typ intlong . Deshalb berücksichtigt der Compiler Konvertierungen für den Typ des Objekts, das abgeglichen wird: UDC. Eine Konvertierung in int ist vorhanden und wird ausgewählt.

Während des Prozesseses zum Zuordnen von Argumenten können Standardkonvertierungen sowohl auf das Argument als auch auf das Ergebnis einer benutzerdefinierten Konvertierung angewendet werden. Daher funktioniert der folgende Code:

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

Im vorherigen Beispiel wird die benutzerdefinierte Konvertierung, operator long,aufgerufen, um in den Typ zu long konvertieren. Wenn keine benutzerdefinierte Konvertierung in den Typ definiert worden wäre, wäre die Konvertierung wie folgt fortgesetzt worden: Der Typ wäre mithilfe der benutzerdefinierten Konvertierung in den longUDC Typ konvertiert int worden. Dann wäre die Standardkonvertierung vom Typ in den Typ so angewendet worden, int dass sie mit dem Argument in der long Deklaration übereinstimmen würde.

Wenn benutzerdefinierte Konvertierungen erforderlich sind, um mit einem Argument zu übereinstimmen, werden die Standardkonvertierungen nicht verwendet, wenn die beste Übereinstimmung bewertet wird. Auch wenn mehr als eine Kandidatenfunktion eine benutzerdefinierte Konvertierung erfordert, werden die Funktionen als gleich betrachtet. Beispiel:

// 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 );
}

Beide Versionen von Func erfordern eine benutzerdefinierte Konvertierung, um den Typ in int das Klassentypargument zu konvertieren. Mögliche Konversionen sind:

  • Konvertieren von Typ int zu Typ UDC1 (benutzerdefinierte Konvertierung).

  • Konvertieren Sie vom Typ in den Typ , und konvertieren Sie dann in den Typ intlongUDC2 (eine zweistufige Konvertierung).

Obwohl die zweite Konvertierung sowohl eine Standardkonvertierung als auch die benutzerdefinierte Konvertierung erfordert, werden die beiden Konvertierungen weiterhin als gleich betrachtet.

Hinweis

Benutzerdefinierte Konvertierungen gelten als Konvertierung durch Konstruktion oder als Konvertierung durch Initialisierung (Konvertierungsfunktion). Beide Methoden werden als gleich betrachtet, wenn die beste Übereinstimmung berücksichtigt wird.

Argumentübereinstimmung und der this-Zeiger

Klassen memberfunktionen werden unterschiedlich behandelt, je nachdem, ob sie als deklariert static sind. Da nicht statische Funktionen über ein implizites Argument verfügen, das den Zeiger liefert, werden nicht statische Funktionen als ein Argument mehr als statische Funktionen betrachtet. Andernfalls werden sie this identisch deklariert.

Diese nicht statischen Memberfunktionen erfordern, dass der implizite Zeiger mit dem Objekttyp übereinstimmen muss, über den die Funktion aufgerufen wird, oder bei überladenen Operatoren, dass das erste Argument mit dem Objekt übereinstimmen muss, auf das der Operator angewendet this wird. (Weitere Informationen zu überladenen Operatoren finden Sie unter Überladene Operatoren.)

Im Gegensatz zu anderen Argumenten in überladenen Funktionen werden keine temporären Objekte eingeführt, und es werden keine Konvertierungen versucht, wenn versucht wird, mit dem this Zeigerargument zu übereinstimmen.

Wenn der Memberauswahloperator für den Zugriff auf eine Memberfunktion der Klasse verwendet wird, hat das ->class_namethis Zeigerargument den Typ class_name * const . Wenn die Member als oder deklariert constvolatile sind, sind die Typen const class_name * constvolatile class_name * const bzw. .

Der Memberauswahloperator . funktioniert genau auf die gleiche Weise, außer dass ein impliziter &-Operator (address-of) dem Objektnamen vorangestellt ist. Im folgenden Beispiel wird gezeigt, wie dies funktioniert:

// Expression encountered in code
obj.name

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

Der linke Operand der Operatoren ->* und .* (Zeiger auf Member) wird hinsichtlich der Argumentübereinstimmung genauso wie der .-Operator und der ->-Operator (Memberauswahl) behandelt.

Ref-Qualifizierer für Memberfunktionen

Ref-Qualifizierer ermöglichen es, eine Memberfunktion auf der Grundlage zu überladen, ob das Objekt, auf das von verwiesen wird, ein rvalue oder this ein lvalue ist. Dieses Feature kann verwendet werden, um unnötige Kopiervorgänge in Szenarien zu vermeiden, in denen Sie keinen Zeigerzugriff auf die Daten bereitstellen. Angenommen, die -Klasse initialisiert einige Daten in ihrem Konstruktor und gibt eine Kopie dieser Daten C in der Memberfunktion get_data() zurück. Wenn ein Objekt vom Typ ein rvalue ist, der zerstört werden soll, wählt der Compiler die Überladung aus, die die Daten verschiebt, anstatt sie Cget_data() && zu kopieren.

#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;
}

Einschränkungen beim Überladen

Mehrere Einschränkungen steuern eine akzeptable Gruppe von überladenen Funktionen:

  • Zwei beliebige Funktionen in einer Gruppe von überladenen Funktionen müssen unterschiedliche Argumentlisten haben.

  • Das Überladen von Funktionen mit Argumentlisten der gleichen Typen auf alleiniger Grundlage des Rückgabetyps ist ein Fehler.

    Microsoft-spezifisch

Sie können den Operator new ausschließlich auf der Grundlage des Rückgabetyps überladen, insbesondere auf der Grundlage des angegebenen Speichermodellmodifizierers.

Ende Microsoft-spezifisch

  • Memberfunktionen können nicht allein aufgrund der statischen und der anderen nicht statischen Funktionen überladen werden.

  • typedef-Deklarationen definieren keine neue Typen. Sie führen Synonyme für vorhandene Typen ein. Sie wirken sich nicht auf den Überladungsmechanismus aus. Betrachten Sie folgenden Code:

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

    Die vorhergehenden zwei Funktionen verfügen über identische Argumentlisten. PSTR ist ein Synonym für den Typ char * . Im Memberbereich generiert dieser Code einen Fehler.

  • Aufgelistete Typen sind verschiedene Typen und können verwendet werden, um zwischen überladenen Funktionen zu unterscheiden.

  • Die Typen "Array von " und "Zeiger auf" gelten für die Unterscheidung zwischen überladenen Funktionen als identisch, jedoch nur für singly dimensionierte Arrays. Aus diesem Grund lösen diese überladenen Funktionen einen Konflikt aus und generieren eine Fehlermeldung:

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

    Für mehrdimensionale Arrays gelten die zweite und alle nachfolgenden Dimensionen als Teil des Typs. Deshalb werden sie beim Unterscheiden zwischen überladenen Funktionen verwendet:

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

Überladen, Überschreiben und Ausblenden

Zwei beliebige Funktionsdeklarationen des gleichen Namens im gleichen Bereich können auf die gleiche Funktion oder zwei einzelne Funktionen, die überladen werden, verweisen. Wenn die Argumentlisten der Deklarationen Argumente äquivalenter Typen enthalten (wie im vorherigen Abschnitt beschrieben), beziehen sich die Funktionsdeklarationen auf die gleiche Funktion. Andernfalls beziehen sie sich auf zwei separate Funktionen, die mithilfe des Überladens ausgewählt werden.

Der Klassenbereich wird streng eingehalten. daher befindet sich eine in einer Basisklasse deklarierte Funktion nicht im selben Bereich wie eine Funktion, die in einer abgeleiteten Klasse deklariert ist. Wenn eine Funktion in einer abgeleiteten Klasse mit dem gleichen Namen wie eine virtuelle Funktion in der Basisklasse deklariert wird, überschreibt die Funktion der abgeleiteten Klasse die Basisklassenfunktion. Weitere Informationen finden Sie unter Virtuelle Funktionen.

Wenn die Basisklassenfunktion nicht als "virtuell" deklariert ist, wird sie von der abgeleiteten Klassenfunktion ausgeblendet. Sowohl das Überschreiben als auch das Ausblenden unterscheiden sich vom Überladen.

Der Blockbereich wird streng eingehalten. daher befindet sich eine im Dateibereich deklarierte Funktion nicht im selben Bereich wie eine lokal deklarierte Funktion. Wenn eine lokal deklarierte Funktion den gleichen Namen wie eine Funktion besitzt, die im Dateibereich deklariert wird, blendet die lokal deklarierte Funktion die Funktion im Dateibereich aus, und es erfolgt keine Überladung. Beispiel:

// 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" );
}

Der vorhergehende Code zeigt zwei Definitionen der Funktion func. Die Definition, die ein Argument vom Typ annimmt, char * ist main aufgrund der -Anweisung lokal extern für . Daher ist die Definition, die ein Argument vom Typ int annimmt, ausgeblendet, und der erste Aufruf von func ist fehlerhaft.

Für überladene Memberfunktionen können verschiedenen Versionen der Funktion unterschiedliche Zugriffsrechte zugewiesen werden. Sie werden weiterhin als im Gültigkeitsbereich der einschließenden Klasse betrachtet und sind somit überladene Funktionen. Betrachten Sie den folgenden Code, in dem die Memberfunktion Deposit überladen wird. Eine Version ist öffentlich, die andere privat.

Der Zweck dieses Beispiels ist es, eine Account-Klasse bereitzustellen, in der ein korrektes Kennwort erforderlich ist, um Eingaben vorzunehmen. Dies erfolgt durch Überladen.

Der Aufruf von Deposit in ruft die private Account::Deposit Memberfunktion auf. Dieser Aufruf ist richtig, da Account::Deposit eine Memberfunktion ist und Zugriff auf die privaten Member der Klasse hat.

// 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;
}

Weitere Informationen

Funktionen (C++)