C++
Funktion Överbelastning
Sök…
Introduktion
Se även separat ämne om överbelastningsupplösning
Anmärkningar
Oklarheter kan uppstå när en typ implicit kan konverteras till mer än en typ och det inte finns någon matchande funktion för den specifika typen.
Till exempel:
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
Vad är funktionsöverbelastning?
Funktionsöverbelastning har flera funktioner som deklarerats i samma omfattning med exakt samma namn finns på samma plats (känd som omfång ) som endast skiljer sig i sin signatur , vilket betyder argumenten de accepterar.
Anta att du skriver en serie funktioner för allmänna utskriftsfunktioner, börjar med std::string
:
void print(const std::string &str)
{
std::cout << "This is a string: " << str << std::endl;
}
Det här fungerar bra, men anta att du vill ha en funktion som också accepterar en int
och skriver ut det också. Du kan skriva:
void print_int(int num)
{
std::cout << "This is an int: " << num << std::endl;
}
Men eftersom de två funktionerna accepterar olika parametrar kan du helt enkelt skriva:
void print(int num)
{
std::cout << "This is an int: " << num << std::endl;
}
Nu har du två funktioner, båda namngivna print
, men med olika signaturer. Den ena accepterar std::string
, den andra en int
. Nu kan du ringa dem utan att oroa dig för olika namn:
print("Hello world!"); //prints "This is a string: Hello world!"
print(1337); //prints "This is an int: 1337"
Istället för:
print("Hello world!");
print_int(1337);
När du har överbelastade funktioner, anger kompilatorn vilken av funktionerna du vill ringa från parametrarna du tillhandahåller den. Man måste vara försiktig när skrivfunktionen är överbelastad. Till exempel med implicita konverteringar:
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;
}
Nu är det inte direkt klart vilken överbelastning av print
heter när du skriver:
print(5);
Och du kan behöva ge din kompilator några ledtrådar, som:
print(static_cast<double>(5));
print(static_cast<int>(5));
print(5.0);
Viss försiktighet måste också tas vid skrivning av överbelastningar som accepterar valfria parametrar:
// 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;
}
Eftersom det inte finns något sätt för kompilatorn att berätta om ett samtal som print(17)
är avsett för den första eller andra funktionen på grund av den valfria andra parametern, kommer detta inte att kompilera.
Returnera typ i överbelastning av funktioner
Observera att du inte kan överbelasta en funktion baserad på dess returtyp. Till exempel:
// WRONG CODE
std::string getValue()
{
return "hello";
}
int getValue()
{
return 0;
}
int x = getValue();
Detta kommer att orsaka ett kompilationsfel eftersom kompilatorn inte kan räkna ut vilken version av getValue
att ringa, även om returtypen tilldelas en int
.
Medlem Funktion cv-kvalificering Överbelastning
Funktioner inom en klass kan överbelastas när de nås via en cv-kvalificerad referens till den klassen; detta används oftast för överbelastning för const
, men kan också användas för att överbelasta för volatile
och const volatile
också. Detta beror på att alla icke-statiska medlemsfunktioner tar this
som en dold parameter, som cv-kvalificeringarna tillämpas på. Detta används oftast för överbelastning för const
, men kan också användas för volatile
och const volatile
.
Detta är nödvändigt eftersom en medlemsfunktion endast kan anropas om den är minst lika cv-kvalificerad som den instans som den heter. Medan en icke- const
instans kan ringa både const
och non- const
medlemmar, kan en const
instans endast ringa const
medlemmar. Detta tillåter en funktion att ha olika beteenden beroende på den anropande instansens cv-kvalificeringar, och tillåter programmeraren att avvisa funktioner för en oönskad cv-kvalificering (er) genom att inte tillhandahålla en version med den eller de kvalificeringarna.
En klass med några grundläggande print
kan const
belastad så här:
#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"
}
Detta är ett viktigt princip för const
korrekthet: Genom att markera medlemsfunktioner som const
får de kallas på const
instanser, vilket i sin tur tillåter funktioner att ta instanser som const
pekare / referenser om de inte behöver ändra dem. Detta tillåter kod att specificera om den modifierar tillstånd genom att ta omodifierade parametrar som const
och modifierade parametrar utan cv-kvalificeringar, vilket gör koden både säkrare och mer läsbar.
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.
}
En vanlig användning av detta är att förklara accessors som const
och mutatorer som non- const
.
Inga klassmedlemmar kan ändras inom en const
medlemsfunktion. Om det finns någon medlem som du verkligen behöver ändra, till exempel att låsa en std::mutex
, kan du förklara den som mutable
:
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;
};