C++
Funktionsüberladung
Suche…
Einführung
Siehe auch separates Thema zur Überlastauflösung
Bemerkungen
Mehrdeutigkeiten können auftreten, wenn ein Typ implizit in mehr als einen Typ konvertiert werden kann und für diesen bestimmten Typ keine übereinstimmende Funktion vorhanden ist.
Zum Beispiel:
void foo(double, double);
void foo(long, long);
//Call foo with 2 ints
foo(1, 2); //Function call is ambiguous - int can be converted into a double/long at the same time
Was ist Funktionsüberladung?
Beim Überladen von Funktionen werden mehrere Funktionen im selben Gültigkeitsbereich deklariert, wobei derselbe Name an derselben Stelle (bekannt als Gültigkeitsbereich ) vorhanden ist und sich nur in der Signatur unterscheidet , dh die von ihnen akzeptierten Argumente.
Angenommen, Sie schreiben eine Reihe von Funktionen für allgemeine Druckfunktionen, beginnend mit std::string
:
void print(const std::string &str)
{
std::cout << "This is a string: " << str << std::endl;
}
Das funktioniert gut, aber Sie wollen eine Funktion, die auch ein int
akzeptiert und das auch druckt. Sie könnten schreiben:
void print_int(int num)
{
std::cout << "This is an int: " << num << std::endl;
}
Da die beiden Funktionen jedoch unterschiedliche Parameter akzeptieren, können Sie einfach schreiben:
void print(int num)
{
std::cout << "This is an int: " << num << std::endl;
}
Jetzt haben Sie 2 Funktionen, beide mit dem Namen print
, jedoch mit unterschiedlichen Signaturen. Einer akzeptiert std::string
, der andere ein int
. Jetzt können Sie sie anrufen, ohne sich um verschiedene Namen zu kümmern:
print("Hello world!"); //prints "This is a string: Hello world!"
print(1337); //prints "This is an int: 1337"
Anstatt:
print("Hello world!");
print_int(1337);
Wenn Sie Funktionen überladen haben, ermittelt der Compiler, welche der Funktionen von den von Ihnen angegebenen Parametern aufgerufen werden soll. Beim Schreiben von Funktionsüberladungen ist Vorsicht geboten. Zum Beispiel bei impliziten Typkonvertierungen:
void print(int num)
{
std::cout << "This is an int: " << num << std::endl;
}
void print(double num)
{
std::cout << "This is a double: " << num << std::endl;
}
Nun ist nicht sofort klar, welche Überladung des print
beim Schreiben aufgerufen wird:
print(5);
Möglicherweise müssen Sie Ihrem Compiler einige Hinweise geben, z.
print(static_cast<double>(5));
print(static_cast<int>(5));
print(5.0);
Beim Schreiben von Überladungen, die optionale Parameter akzeptieren, ist auch etwas Vorsicht geboten:
// WRONG CODE
void print(int num1, int num2 = 0) //num2 defaults to 0 if not included
{
std::cout << "These are ints: << num1 << " and " << num2 << std::endl;
}
void print(int num)
{
std::cout << "This is an int: " << num << std::endl;
}
Da der Compiler aufgrund des optionalen zweiten Parameters nicht feststellen kann, ob ein Aufruf wie print(17)
für die erste oder zweite Funktion bestimmt ist, wird der Compiler nicht kompiliert.
Rückgabetyp bei Überladen der Funktion
Beachten Sie, dass Sie eine Funktion nicht basierend auf ihrem Rückgabetyp überladen können. Zum Beispiel:
// WRONG CODE
std::string getValue()
{
return "hello";
}
int getValue()
{
return 0;
}
int x = getValue();
Dies verursacht einen Kompilierungsfehler, da der Compiler nicht herausfinden kann, welche Version von getValue
aufgerufen werden soll, obwohl der Rückgabetyp einem int
zugewiesen ist.
Member Function cv-qualifier Überladung
Funktionen innerhalb einer Klasse können überlastet werden, wenn auf sie über eine von cv qualifizierte Referenz auf diese Klasse zugegriffen wird. Dies wird am häufigsten für Überladungen von const
, kann aber auch für volatile
und const volatile
werden. Dies liegt daran, dass alle nicht statischen Elementfunktionen this
verborgenen Parameter annehmen, auf den die cv-qualifiers angewendet werden. Dies wird am häufigsten für Überladung von const
, kann aber auch für volatile
und const volatile
.
Dies ist notwendig, da eine Memberfunktion nur aufgerufen werden kann, wenn sie mindestens so hoch ist wie die Instanz, für die sie aufgerufen wird. Während eine Nicht- const
Instanz sowohl const
als auch nicht- const
Member aufrufen kann, kann eine const
Instanz nur const
Member aufrufen. Dadurch kann eine Funktion je nach den CV-Qualifikationsmerkmalen der aufrufenden Instanz ein unterschiedliches Verhalten aufweisen, und der Programmierer kann Funktionen für unerwünschte CV-Qualifikationsmerkmale verbieten, indem keine Version mit diesen Qualifikationsmerkmalen bereitgestellt wird.
Eine Klasse mit einigen grundlegenden print
könnte const
wie so überlastet:
#include <iostream>
class Integer
{
public:
Integer(int i_): i{i_}{}
void print()
{
std::cout << "int: " << i << std::endl;
}
void print() const
{
std::cout << "const int: " << i << std::endl;
}
protected:
int i;
};
int main()
{
Integer i{5};
const Integer &ic = i;
i.print(); // prints "int: 5"
ic.print(); // prints "const int: 5"
}
Dies ist ein wesentlicher Grundsatz der const
Korrektheit: Durch das Markieren von Member-Funktionen als const
können sie für const
Instanzen aufgerufen werden, const
Funktionen Instanzen als const
Zeiger / Referenzen verwenden können, wenn sie nicht geändert werden müssen. Dadurch kann der Code angeben, ob er den Status ändert, indem er nicht modifizierte Parameter als const
und modifizierte Parameter ohne cv-qualifiers verwendet, wodurch Code sicherer und lesbarer wird.
class ConstCorrect
{
public:
void good_func() const
{
std::cout << "I care not whether the instance is const." << std::endl;
}
void bad_func()
{
std::cout << "I can only be called on non-const, non-volatile instances." << std::endl;
}
};
void i_change_no_state(const ConstCorrect& cc)
{
std::cout << "I can take either a const or a non-const ConstCorrect." << std::endl;
cc.good_func(); // Good. Can be called from const or non-const instance.
cc.bad_func(); // Error. Can only be called from non-const instance.
}
void const_incorrect_func(ConstCorrect& cc)
{
cc.good_func(); // Good. Can be called from const or non-const instance.
cc.bad_func(); // Good. Can only be called from non-const instance.
}
Eine gemeinsame Nutzung hierfür ist Accessoren als erklärt const
und Mutatoren als nicht const
.
Innerhalb einer const
können keine Klassenmitglieder geändert werden. Wenn es ein Mitglied gibt, das Sie wirklich ändern müssen, z. B. das Sperren eines std::mutex
, können Sie es als mutable
deklarieren:
class Integer
{
public:
Integer(int i_): i{i_}{}
int get() const
{
std::lock_guard<std::mutex> lock{mut};
return i;
}
void set(int i_)
{
std::lock_guard<std::mutex> lock{mut};
i = i_;
}
protected:
int i;
mutable std::mutex mut;
};