Přeplňování operátorů v jazyce C ++: základy, příklady

Ve všech vědách existují standardní notace, které usnadňují pochopení myšlenek. Například v matematice je násobení, dělení, sčítání a další symbolická notace. Výraz (x + y * z) je mnohem snazší pochopit než "násobit y, z a přidat do x". Představte si, že až do XVI. Století matematika neměla symbolickou notaci, všechny výrazy byly psány slovně, jako by to byl umělecký text s popisem. A obvyklé známky operací pro nás se objevily později. Je obtížné přeceňovat význam krátkého záznamu. Na základě těchto úvah byly programovací jazyky přidány k přetížení operátorů. Zvažte příklad.


Příklad přetížení operátorů

Téměř jako jakýkoli jazyk podporuje C ++ množství operátorů pracujících s datovými typy zabudovanými do standardního jazyka. Většina programů však používá speciální typy k řešení určitých úkolů. Například komplexní matematika nebo maticová algebra jsou implementovány v programu reprezentováním komplexních čísel nebo matic v typů C ++. Vestavěné operátory nejsou schopny distribuovat svou práci a provádět potřebné procedury v určitých třídách, ať už je to zřejmé. Proto je pro přidávání matric například obvykle vytvořena samostatná funkce. Je zřejmé, že volání sum_matrix (A, B) v kódu bude mít méně jasný charakter než výraz A + B.
Zvažte přibližnou třídu komplexních čísel:

//si představte komplexní číslo ve formě dvojice čísel splovoucí bod.
třída komplex {{12} double re, im;
veřejný:
komplex (dvojitý r, dvojitý i): re (r), im (i) {} //konstruktor
komplexní operátor + (komplex); //komplexní operátor * (komplex); //přetížení násobení
};

void main () {
komplexní {1 2}, b {3}}, c {0}};
c = a + b;
c = operátor + (b); ////funkce operátora může být nazývána libovolná funkce, tato položka je ekvivalentní k + b
c = a * b + komplexu (1 3); //provádí obvyklá pravidla prioritních operací sčítání a násobení
}

Stejně tak lze provést, jako je například operátor přetížení /O v jazyce C ++ a přizpůsobit je pro zobrazení takové složité struktury jako šablony.

Provozovatelé k dispozici pro přetížení

Úplný seznam všech provozovatelů, pro které lze použít přetížení:

nový []

+

-

*

/

%

^

~

!

=

/p>

>

+ =

- =

* =

/=

% =

^ =

and =

| =

=

=

==

! =

> =

|

++

-

- & gt; *

,

->

vymazat

Jak je patrné z tabulky, přetížení je přípustné pro většinu operátorů jazyků. Není třeba přetížit obsluhu. To se provádí pouze pro pohodlí. Proto chybí například přetížení operátorů v Javě. A teď o tak důležitém okamžiku.

Provozovatelé, jejichž přetížení je zakázáno

  • Povolení k viditelnosti - «::»;
  • Volba člena je ".";
  • Výběr člena pomocí ukazatele na člena - ". *";
  • Třetí podmíněný operátor - «?:»;
  • velikost operátora;
  • Typový operátor.

Správným operantem dat operátorů je název, ne hodnota. Povolení jejich přetížení by proto mohlo vést k mnoha nejednoznačným návrhům a značně by komplikovalo životy programátorů. Přestože existuje mnoho programovacích jazyků, které umožňují přetížení všech operátorů - například přetížení operátorů Pythonu.


Omezení operátor přetížení

  • Nemůžete změnit binární operátor na unary a naopak, protože nemůžete přidat třetí operand.
  • Nemůžete vytvořit jiné operátory než ty, které jsou. Toto omezenípodporuje odstranění mnoha nejednoznačností. Pokud je potřeba nový operátor, můžete použít funkci, která pro tyto účely provede požadovanou akci.
  • Funkce operátora může být buď členem třídy, nebo má alespoň jeden typový argument. Výjimkou jsou noví operátoři a operátoři mazání. Toto pravidlo zakazuje změnu významu výrazů, pokud neobsahují uživatelem definované typy objektů. Zejména nelze vytvořit funkci operátora, která by fungovala pouze s ukazateli nebo nucena operátora doplňku pracovat jako násobení. Výjimkou jsou operátoři "=", "& amp;" a "," pro objekty třídy.
  • Funkce operátora s prvním termínem, který patří k jednomu z vestavěných typů dat v jazyce C ++, nemůže být členem třídy.
  • Název každé funkce operátora začíná operátorem klíčového slova, po němž následuje symbolické označení samotného operátora.
  • Vestavěné operátory jsou definovány tak, že mezi nimi existuje spojení. Následující operátory jsou například vzájemně ekvivalentní: ++ x; x + = 1; x = x + 1. Po nové definici se spojení mezi nimi nezachová. O zachování své práce společně s novými typy programátorů se budou muset postarat o sebe.
  • Kompilátor nemůže myslet. Výrazy z + 5 a 5 + z (kde z - komplexní číslo) bude překladačem zvažováno různými způsoby. První je "komplexní + číslo" a druhé je "číslo + složité". Proto pro každý výraz potřebujete definovat vlastní prohlášení o přidání.
  • Když hledá definici operátora, překladač nedává přednost žádným členům třídy, ani pomocným funkcím,které jsou definovány mimo třídu. Pro překladatele jsou stejné.

Interpretace binárních a unárních operátorů.

binární operátor je definován jako člen funkce jedné proměnné nebo funkce dvou proměnných. Pro každý binární vyjádření obsluhy @ s @ b @ jen struktur:


teplotě nižší než script type = "text /javascript" & gt;
var blockSettings3 = {blockId: "R-A-70350-3", renderTo "yandex_rtb_R-A-70350-3", async :! 0};

, pokud (document.cookie.indexOf ("abmatch =") větší nebo rovno 0) {
blockSettings3 = {blockId: "RA-70350-3", renderTo „yandex_rtb_R-A-70350- 3 ", statId: 70350async:! 0};
}

! Funkce (a, b, c, d, e) {a [c] = a [c] || [], se [C] .push (funkce () {Ya .Context.AdvManager.render (blockSettings3)}), e = b.getElementsByTagName ("scénář") , d = b.createElement ("scénář"), d.type = "text /javascript", d.src = "//an.yandex.ru/system/context.js",d.async=!0e.parentNode.insertBefore(d,e)}(this,this.document,"yandexContextAsyncCallbacks");

a.operátor @ (b) nebo operátor @ (a, b).

Zvažte příklad třídy komplexních čísel pro definici operací jako členů třídy a pomocných.

třída komplex {{174} double re, im;
veřejnost:
komplexní & operátor + = (komplex z);
komplexní & operátor * = (komplex z);
};
//pomocné funkce
komplexní operátor + (komplex z1 komplex z2);
komplex operátor + (komplex z, dvojitý a);

, které operátory bude vybrán a být volen obecně určen vnitřních mechanismů jazyka, které budou popsány níže. Obvykle se to děje podle typů.

​​

Vyberte popsat funkci jako člen třídy nebo mimo ni - je, obecně, chuť. Ve výše uvedeném příkladu je princip výběru byla následující: v případě, že operace změní levý operand (například A + = b), a pak jej zaznamenat ve třídě a pomocí měnitelným převodem při, pro jeho bezprostřední změny; pokud operace není nicmodifikuje a jednoduše vrací novou hodnotu (například a + b) - za definici třídy.

Definice unarnыh operátoři přetížení v C ++ děje stejným způsobem, s tím rozdílem, že se dělí na dva typy:

  • operátor prefixu, umístěný na operandu, - @a, například i ++. o Definováno jako a.operátor @ () nebo operátor @ (aa);
  • operátor postfixu, umístěný za operandem, - b @, například i ++. o definováno jako b.operator @ (int) nebo operátor @ (b, int)

Stejným způsobem jako pro binární operátory, když reklama je operátor ve třídě a výběru mimo třída mechanismy budou realizovány C ++.

Pravidla výběru operátora

Nechť binární operátor @ x je aplikován na objektech třídy X a třídy Y. y pravidel pro umožnění X- Y bude následující:

  1. je-li X třídou, v něm hledá definici operátora @ jako výraz X nebo základní třídu X;
  2. pro zobrazení kontextu, ve kterém je umístěn výraz x @ y;
  3. pokud X patří do jmenného prostoru N, vyhledejte příkaz operátora N;
  4. Pokud Y patří do jmenného prostoru M, vyhledejte příkaz operátora M.

Pokud bylo nalezeno několik prohlášení provozovatele operátora @ v 1-4, volba bude provedena podle pravidel povolení přeplněných funkcí.

Hledání jednorázových operátorů probíhá přesně stejným způsobem.

Upřesnit definici komplexu třídy

Nyní budeme sestavit třídu komplexních čísel podrobnějiprokázat řadu dříve oznámených pravidel.

třídový komplex {
double re, im;
veřejnost:
komplexní & operátor + = (komplex z) {//pracuje s výrazy formuláře z1 + = z2
re + = z.re;
im + = z.im;
vrátí * toto;
}
komplexní & operátor + = (double a) {//pracuje s výrazy formuláře z1 + = 5;
re + = a;
vrátit * toto;
}
komplex (): re

, im

{} //konstruktor pro implicitní inicializaci. Takže všechna deklarovaná celočíselná čísla budou mít počáteční hodnoty (0 0)
složité (dvojité r): re (r), im

{} //konstruktor činí vyjádření formy komplexu z = 11 možné; ekvivalentní záznamu z = komplex
;
komplex (dvojitý r, dvojitý i): re (r), im (i) {} //konstruktor
};
komplexní operátor + (komplex z1 komplex z2) {//pracuje s výrazy formu z1 + z2
komplex res = z1;
návrat res + = z2; //používá operátor definovaný jako členská funkce
}
komplexní operátor + (komplex z, dvojitý a) {//zpracovává výrazy formuláře z + 2
komplex res = z;
návrat res + = a;
}
komplexní operátor + (dvojité a, komplex z) {//zpracovává výrazy formuláře 7 + z
komplex res = z;
návrat res + = a;
}
//


Jak lze vidět z kódu, přetížení operátorů má poměrně komplikovaný mechanismus, který může výrazně růst. Takový podrobný přístup však umožňuje přetížení i pro velmi složité datové struktury. Například přetížení operátorů C ++ ve třídě šablon. Takové vytváření funkcí pro každého a vše může být zdlouhavé a vést k chybám. Například pokud přidáte třetí typ zvažovaných funkcí, budete muset zvážit operace z důvodů kombinace tří typů. Budeme muset napsat 3 funkce s jedním argumentem, 9 - s dvěma a 27 - se třemi. Proto, v některých případech, provádění všech těchto funkcí a jejich významné sníženímnožství lze dosáhnout použitím konverze typů.

Zvláštní operátory

Operátor indexování "[]" by měl být vždy definován jako člen třídy, protože snižuje chování objektu na matici. V tomto případě argument indexování může být jakéhokoli typu, který umožňuje například vytvářet asociativní pole. Operátor volání funkce () může být považován za binární operaci. Například v konstrukci "výraz (seznam výrazů)" bude levý operand binární operace () "výraz" a pravý je seznam výrazů. Funkce operátor () () musí být členem třídy. Operátor sekvence "," (čárka) je volán pro objekty, pokud je vedle nich čárka. Operátor se však nepodílí na přenosu argumentů funkce. Operátor pojmenování "->" musí být také definován jako člen funkce. Ve svém obsahu může být definován jako unary operátor postfix. Současně musí nutně vrátit buď odkaz nebo ukazatel, který umožňuje přístup k objektu. Operátor přiřazení je také definován jako člen třídy vzhledem k jeho spojení s levým operandem. Operátory přiřazení «=», adresy «& amp;;» a sekvence «,» musí být definovány ve veřejném bloku.

Výsledek

Přetížení obsluhy pomáhá realizovat jeden z klíčových aspektů PLO na polymorfismu. Je však důležité pochopit, že přetížení není víc než jiný způsob volání funkcí. Úkolem přetížení operátorů je často zlepšit porozumění kódu než zajistit výhra v jakýchkoli problémech.
A to není všechno. Rovněž je třeba mít na paměti, že přetížení operátorů je složitým mechanismem s množstvím nástrah. Je proto velmi snadné udělat chybu. To je hlavním důvodem, proč většina programátorů doporučuje, aby se zdržely používání přetížení provozovatele a uchýlila se k němu pouze v extrémních případech as plnou důvěrou ve své kroky.

Doporučení

  • Proveďte přetížení operátora pouze za účelem simulace běžného záznamu. Pro vytvoření kódu čtení. Pokud se kód stane obtížnější strukturou nebo čitelností, měli byste odmítnout přetížení operátorů a používat funkce.
  • U velkých operandů slouží k úsporám místa argumenty pro zadávání s konstantními odkazy pro přenos.
  • Optimalizujte návratové hodnoty.
  • Nedotýkejte se kopírování, pokud je vhodná pro vaši třídu.
  • Není-li výchozí kopie vhodná, upravte nebo výslovně zakázat kopírování.
  • Funkce členství by měly být upřednostňovány před nečlenskými funkcemi v případech, kdy funkce vyžadují přístup k zastupování třídy.
  • Uveďte jmenný prostor a označte vztah mezi funkcemi třídy.
  • Použití nečlenských funkcí pro symetrické operátory.
  • Použijte operátor () pro indexy v vícerozměrných polích.
  • Buďte opatrní s implicitními transformacemi.
  • Související publikace