Přetížení operátora - Operator overloading

V počítačovém programování je přetížení operátorů , někdy nazývané operátor ad hoc polymorfismus , specifickým případem polymorfismu , kde různí operátoři mají různé implementace v závislosti na svých argumentech. Přetížení operátora je obecně definováno programovacím jazykem , programátorem nebo obojím.

Zdůvodnění

Přetížení operátoru je syntaktický cukr a používá se, protože umožňuje programování pomocí zápisu blíže k cílové doméně a umožňuje uživatelsky definovaným typům podobnou úroveň syntaktické podpory jako typy integrované do jazyka. Je to běžné například ve vědeckých počítačích, kde umožňuje manipulaci s výpočetními reprezentacemi matematických objektů se stejnou syntaxí jako na papíře.

Přetížení operátora nemění výrazovou sílu jazyka (s funkcemi), protože jej lze emulovat pomocí volání funkcí. Uvažujme například proměnné a, ba cnějaké uživatelem definovaného typu, jako je například matice :

a + b * c

V jazyce, který podporuje přetížení operátora, as obvyklým předpokladem, že operátor '*' má vyšší prioritu než operátor '+', je to stručný způsob psaní:

Add(a, Multiply(b, c))

Bývalá syntaxe však odráží běžné matematické využití.

Příklady

V tomto případě je operátor sčítání přetížen, aby umožnil přidání na uživatelsky definovaný typ Timev C ++ :

Time operator+(const Time& lhs, const Time& rhs) {
  Time temp = lhs;
  temp.seconds += rhs.seconds;
  temp.minutes += temp.seconds / 60;
  temp.seconds %= 60;
  temp.minutes += rhs.minutes;
  temp.hours += temp.minutes / 60;
  temp.minutes %= 60;
  temp.hours += rhs.hours;
  return temp;
}

Sčítání je binární operace , což znamená, že má dva operandy . V C ++ jsou předávané argumenty operandy a tempobjekt je vrácená hodnota.

Operaci lze také definovat jako metodu třídy, nahrazenou lhsskrytým thisargumentem; To však nutí levý operand být typu Time:

// The "const" right before the opening curly brace means that |this| is not modified.
Time Time::operator+(const Time& rhs) const {
  Time temp = *this;  // |this| should not be modified, so make a copy.
  temp.seconds += rhs.seconds;
  temp.minutes += temp.seconds / 60;
  temp.seconds %= 60;
  temp.minutes += rhs.minutes;
  temp.hours += temp.minutes / 60;
  temp.minutes %= 60;
  temp.hours += rhs.hours;
  return temp;
}

Všimněte si, že unární operátor definovaný jako metoda třídy neobdrží žádný zjevný argument (funguje pouze od this):

bool Time::operator!() const {
  return hours == 0 && minutes == 0 && seconds == 0;
}

Operátor menší než (<) je často přetížen, aby seřadil strukturu nebo třídu:

class Pair {
 public:
  bool operator<(const Pair& p) const {
    if (x_ == p.x_) {
      return y_ < p.y_;
    }
    return x_ < p.x_;
  }

 private:
  int x_;
  int y_;
};

Stejně jako u předchozích příkladů se v posledním příkladu přetížení operátoru provádí v rámci třídy. V C ++ lze po přetížení operátoru méně než (<) použít k třídění některých tříd standardní třídicí funkce .

Kritika

Přetížení operátora bylo často kritizováno, protože umožňuje programátorům znovu přiřadit sémantiku operátorů v závislosti na typech jejich operandů. Například použití <<operátoru v C ++ posune bity v proměnné vlevo o bity, pokud a jsou celočíselného typu, ale pokud je výstupním proudem, výše uvedený kód se pokusí zapsat a do proudu. Protože přetížení operátorem umožňuje původnímu programátorovi změnit obvyklou sémantiku operátora a zaskočit všechny následné programátory, je považováno za vhodné používat přetížení operátora opatrně (tvůrci Javy se rozhodli tuto funkci nepoužívat, i když ne nutně z tohoto důvodu). a << bababab

Další, jemnější problém operátorů je, že určitá pravidla z matematiky lze nesprávně očekávat nebo neúmyslně předpokládat. Například komutativita + (tj. Že a + b == b + a) neplatí vždy; příkladem toho je situace, kdy jsou operandy řetězce, protože + je běžně přetíženo, aby provedlo zřetězení řetězců (tj. "bird" + "song"výnosů "birdsong", zatímco "song" + "bird"výnosů "songbird"). Typický protiklad tohoto argumentu pochází přímo z matematiky: Zatímco + je komutativní na celá čísla (a obecně jakékoli komplexní číslo), není komutativní pro jiné „typy“ proměnných. V praxi + ani není vždy asociativní , například s hodnotami s plovoucí desetinnou čárkou kvůli chybám zaokrouhlení. Jiný příklad: V matematice je násobení komutativní pro reálná a komplexní čísla, ale není komutativní v maticovém násobení .

Katalog

Klasifikace některých běžných programovacích jazyků se provádí podle toho, zda jsou jejich operátory přetížitelné programátorem a zda jsou operátoři omezeni na předdefinovanou sadu.

Operátoři Není přetížitelný Přetížitelný
Nové definovatelné
Omezená sada

Časová osa přetížení operátora

60. léta 20. století

Specifikace ALGOL 68 umožňovala přetížení operátora.

Výpis ze specifikace jazyka ALGOL 68 (strana 177), kde jsou definovány přetížené operátory ¬, =, ≠ a abs :

10.2.2. Operations on Boolean Operands
a) op ∨ = (bool a, b) bool:( a | true | b );
b) op ∧ = (bool a, b) bool: ( a | b | false );
c) op ¬ = (bool a) bool: ( a | false | true );
d) op = = (bool a, b) bool:( a∧b ) ∨ ( ¬b∧¬a );
e) op ≠ = (bool a, b) bool: ¬(a=b);
f) op abs = (bool a)int: ( a | 1 | 0 );

Všimněte si, že k přetížení operátora není potřeba žádná zvláštní deklarace a programátor může vytvářet nové operátory.

80. léta 20. století

Ada podporuje přetížení operátorů od svého vzniku, a to vydáním jazykového standardu Ada 83. Jazykoví designéři se však rozhodli vyloučit definici nových operátorů. Přetíženy mohou být pouze existující operátory v jazyce, a to definováním nových funkcí pomocí identifikátorů jako „+“, „*“, „&“ atd. Následné revize jazyka (v letech 1995 a 2005) zachovávají omezení přetížení existujících operátorů .

V C ++ je přetížení operátora jemnější než v ALGOL 68 .

90. léta 20. století

Návrháři jazyka Java ve společnosti Sun Microsystems se rozhodli přetížení vynechat.

Ruby umožňuje přetížení operátora jako syntaktický cukr pro volání jednoduchých metod.

Lua umožňuje přetížení operátora jako syntaktický cukr pro volání metod s přidanou funkcí, že pokud první operand nedefinuje tohoto operátora, bude použita metoda pro druhý operand.

2000s

Microsoft přidal přetížení operátora do C# v roce 2001 a do Visual Basic .NET v roce 2003.

Scala považuje všechny operátory za metody a umožňuje tak přetížení operátora proxy.

V Raku je definice všech operátorů delegována na lexikální funkce, a tak lze pomocí definic funkcí přetížit operátory nebo přidat nové operátory. Například funkce definovaná ve zdroji Rakudo pro inkrementaci objektu Date pomocí „+“ je:

multi infix:<+>(Date:D $d, Int:D $x) {
    Date.new-from-daycount($d.daycount + $x)
}

Vzhledem k tomu, „multi“ byl používán, funkce budou přidány do seznamu multidispatch kandidátů, a „+“ je přetížen pouze pro případ, kdy jsou splněny typu omezení v podpisu funkce. Zatímco kapacita pro přetížení zahrnuje + , * , > = , postfix a výraz i atd., Umožňuje také přetížení různých operátorů složených závorek: " [ x, y ] ", "x [ y ] ", "x { y } "a" x ( y ) ".

Kotlin podporuje přetížení operátora od jeho vytvoření.

Viz také

Reference