Użycie w tekście programu operatorów (takich jak ' +', ' ->', ' &&' itd.) powoduje tak naprawdę wywołanie funkcji z argumentami będącymi wartościami wyrażeń stojących po obu ich stronach (lub po jednej, dla operatorów jednoargumentowych). Tak więc podczas opracowania instrukcji
c = a + b;wywołana zostanie funkcja mająca na celu dodanie a i b. Jaka to będzie funkcja, zależy od typu a i b; jeśli oba argumenty są typu double, to wywołana zostanie inna funkcja niż ta służąca do dodania dwóch liczb typu int. Jeśli a jest typu int, a b typu double, to, jak wiemy, najpierw wartość zmiennej a (ale nie sama zmienna!) zostanie skonwertowana do typu double i następnie zostanie wywołana funkcja dodająca wartości typu double (i wynik będzie też tego typu). Automatyczne konwersje i wybór odpowiedniej funkcji będą przebiegać „samoczynnie” dla typów wbudowanych. Dla argumentów będących obiektami klas przez nas samych zdefiniowanych, znaczenie tych operatorów jest jednak niezdefiniowane. Zatem, jeśli chcemy korzystać z tych operatorów w takim kontekście, musimy je sami określić.
Możemy to zrobić określając (w specjalny sposób) w definiowanej przez nas klasie odpowiednie metody lub definiując przeznaczone do tego celu funkcje globalne, najczęściej, choć nie jest to konieczne, zaprzyjaźnione z naszą klasą.
Taką operację nazywamy przeciążaniem lub przeładowaniem operatora, w analogi do przeciążania funkcji, czyli definiowania kilku funkcji o tej samej nazwie. Tak jak dla funkcji, właściwa wersja operatora znajdowana jest przez kompilator na podstawie typu argumentów.
Operatory, które mogą być przeciążone (przeładowane), to:
+ | - | * | / | % | ^ | & | | | ∼ | ! |
= | < | > | += | -= | *= | /= | %= | ^= | &= |
|= | = | = | == | != | <= | >= | && | ||
++ | - - | , | ->* | -> | new | delete | () | [] |
przy czym operatory ' &', ' *', ' -', ' +' mogą być przeładowane w obu wersjach: jedno- i dwuargumentowej. Jednoargumentowe operatory zwiększenia i zmniejszenia, '++' i '- -', można przeładowywać z kolei w obu ich odmianach: przyrostkowej i przedrostkowej. Operatory ' =', ' ->',' ()' i ' []' są w pewien sposób specjalne i nimi zajmiemy się osobno. Przeciążać można też operatory ' new' i ' delete', ale jest to kwestia dość delikatna i często zależna od implementacji; nie będziemy się zatem nią zajmować.
Nie można natomiast przeciążać operatorów ' .', ' .*', ' ::', ' ?:' i sizeof.
Niektóre operatory są dostarczane przez system dla (prawie) każdej klasy, nawet jeśli sami nie przeciążyliśmy ich w żaden sposób. Należą do nich operatory ' &' (pobrania adresu), ' =' (przypisania), ' ,' (przecinek) oraz new i delete. Pierwszego lepiej nie przeciążać, ostatnich trzech zwykle nie ma potrzeby.
Jakkolwiek nie przeciążalibyśmy operatorów, ich priorytety i sposób wiązania (od lewej do prawej lub od prawej do lewej, patrz rozdział o operatorach ) pozostają takie same jak dla standardowych operatorów oznaczonych tym samym symbolem. Tak więc w wyrażeniu
a = b + c * d;operacja przypisana symbolowi ' *' zastosowana zostanie przed operacją przypisaną symbolowi ' +', niezależnie od tego, jak zdefiniowane są w tym kontekście te dwie operacje. Podobnie, niezależnie od tego, jak przedefiniujemy operator przypisania ' =', wyrażenie
a = b = c;jest równoważne wyrażeniu
a = ( b = c );a nie
( a = b ) = c;bo operator przypisania wiąże od prawej.
Również liczba argumentów przeciążonego operatora nie jest dowolna: istnieje na przykład tylko jedna standardowa wersja operatora '%' (reszta z dzielenia), w której operator ten jest dwuargumentowy. Nie można zatem przeciążyć tego operatora tak, aby był jednoargumentowy. A więc operatory dwuargumentowe w wersji przeładowanej muszą być też dwuargumentowe, a jednoargumentowe — jednoargumentowe.
Przeładowując operatory należy starać się stosować zasadę of least astonishment — najmniejszego zaskoczenia — tzn. tak przeładowywać operatory, by można było łatwo przewidzieć ich zachowanie na podstawie analogii z normalnym znaczeniem danego operatora. Na przykład, jeśli przeładowywujemy operator dodawania, to zwracany wynik powinien być typu tego samego co argumenty i powinien nie być l-wartością (a zatem na przykład nie powinien zwracać wyniku przez referencję). Dodawanie bowiem nie powinno „dać się postawić” po lewej stronie przypisania.
Podobnie, jeśli przeładowaliśmy operator ' +', to powinniśmy przeciążyć również operator ' +=' i pomyśleć, czy nie byłoby naturalne przeładować też odejmowanie ('-'), odejmowanie z przypisaniem (' -=') i/lub operatory zmniejszenia i zwiększenia.
Z drugiej strony, gdybyśmy definiowali klasę String naśladującą klasę String lub StringBuilder z Javy, naturalne byłoby zastąpienie funkcji scalającej napisy (konkatenacji) przeładowanym operatorem dodawania: w tym przykładzie nie byłoby natomiast intuicyjnie oczywistej interpretacji odejmowania czy zmniejszania.
T.R. Werner, 21 lutego 2016; 20:17