Funkciók túlterhelése és újradefiniálása c. Inline (beágyazott) függvények. Funkció túlterhelés. Érvek alapértelmezett értékekkel

A függvénytúlterhelés több (két vagy több) függvény definiálása azonos névvel, de eltérő paraméterekkel. A túlterhelt funkciók paraméterkészletei sorrendben, számban és típusban eltérőek lehetnek. Így a funkció túlterhelésére azért van szükség, hogy elkerüljük a hasonló műveleteket végrehajtó, de eltérő programlogikával rendelkező függvények nevének megkettőzését. Vegyük például a areaRectangle() függvényt, amely egy téglalap területét számítja ki.

Float areaRectangle(float, float) //függvény, amely egy téglalap területét számítja ki két paraméterrel a(cm) és b(cm) ( return a * b; // megszorozzuk a téglalap oldalainak hosszát és visszatérünk a kapott termék)

Tehát ez egy két float típusú paraméterrel rendelkező függvény, és a függvénynek átadott argumentumoknak centiméterben kell lenniük, a float típusú visszatérési értéke is centiméterben van megadva.

Tegyük fel, hogy kiindulási adataink (téglalap oldalai) méterben és centiméterben vannak megadva, például: a = 2m 35 cm; b = 1m 86 cm Ebben az esetben célszerű négy paraméterű függvényt használni. Vagyis a téglalap oldalainak minden hosszát két paraméterben adjuk át a függvénynek: méterben és centiméterben.

Float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // függvény, amely kiszámolja egy téglalap területét 4 paraméterrel a(m) a(cm); b(m) b(cm) (vissza (a_m * 100 + a_sm) * (b_m * 100 + b_sm); )

A függvény törzsében a méterben átadott értékeket (a_m és b_m) centiméterekre konvertálják, és hozzáadják az a_sm b_sm értékekhez, majd megszorozzuk az összegeket, és megkapjuk a téglalap területét cm-ben persze lehetett az eredeti adatokat centire konvertálni és az első függvényt használni, de most nem erről van szó.

Most az a legfontosabb, hogy két függvényünk van, különböző aláírásokkal, de azonos névvel (túlterhelt függvények). Az aláírás egy függvény nevének és paramétereinek kombinációja. Hogyan lehet ezeket a függvényeket hívni? És a túlterhelt függvények hívása nem különbözik a szokásos függvények hívásától, például:

TerületTéglalap(32, 43); // egy függvényt hívunk meg, amely kiszámolja egy téglalap területét két paraméterrel: a(cm) és b(cm) areaRectangle(4, 43, 2, 12); // egy függvényt hívunk meg, amely kiszámítja egy téglalap területét 4 paraméterrel a(m) a(cm); b(m) b(cm)

Mint látható, a fordító önállóan választja ki a kívánt függvényt, csak a túlterhelt függvények aláírásait elemzi. A függvény túlterhelését megkerülve egyszerűen deklarálhatna egy függvényt más néven, és az jól ellátná a feladatát. De képzelje el, mi történik, ha kettőnél több ilyen funkcióra van szüksége, például 10-re. És mindegyikhez ki kell találnia egy értelmes nevet, és a legnehezebb megjegyezni őket. Pontosan ezért egyszerűbb és jobb a funkciók túlterhelése, hacsak persze nincs rá igény. A program forráskódja alább látható.

#include "stdafx.h" #include << "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1 cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2 return 0; } // перегруженная функция 1 float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см) { return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение } // перегруженная функция 2 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм) { return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); }

// kód Code::Blocks

// Dev-C++ kód

#beleértve névtér használata std; // túlterhelt függvények prototípusai float areaRectangle(float a, float b); float areaRectangle(float a_m, float a_sm, float b_m, float b_sm); int main() ( cout<< "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1 cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2 return 0; } // перегруженная функция 1 float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см) { return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение } // перегруженная функция 2 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм) { return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); }

A program eredménye az 1. ábrán látható.

Amikor függvényeket határoz meg a programjaiban, meg kell adnia a függvény visszatérési típusát, valamint a paraméterek számát és az egyes paraméterek típusát. A múltban (ha C-ben programozott), amikor volt egy add_values ​​nevű függvénye, amely két egész számon működött, és hasonló függvényt akart használni három egész érték hozzáadásához, akkor létre kellett volna hoznia egy függvényt más név. Használhatja például az add_two_values ​​​​és a add_three_values. Hasonlóképpen, ha egy hasonló függvényt szeretne használni lebegőértékek hozzáadásához, akkor egy másik, más nevű függvényre lesz szüksége. A függvénykettőzés elkerülése érdekében a C++ lehetővé teszi több, azonos nevű függvény definiálását. A fordítás során a C++ figyelembe veszi az egyes függvények által használt argumentumok számát, majd pontosan a kívánt függvényt hívja meg. Ha a fordító több funkció közül választhat, azt túlterhelésnek nevezzük. Ebből az oktatóanyagból megtudhatja, hogyan kell használni a túlterhelt függvényeket. A lecke végére a következő alapfogalmakat sajátítja el:

A funkciótúlterhelés lehetővé teszi, hogy ugyanazt a nevet használjuk több, különböző paramétertípusú függvényhez.

A függvények túlterheléséhez egyszerűen definiáljon két azonos nevű és visszatérési típusú függvényt, amelyek a paraméterek számában vagy típusában különböznek egymástól.

A függvénytúlterhelés a C++ nyelv olyan jellemzője, amely nem található meg a C nyelvben. Mint látni fogja, a funkciótúlterhelés meglehetősen kényelmes, és javíthatja a programok olvashatóságát.

ELSŐ BEVEZETÉS A FUNKCIÓ TÚLTERHELÉSÉHEZ

A függvénytúlterhelés lehetővé teszi a programok számára, hogy több függvényt definiáljanak azonos névvel és visszatérési típussal. Például a következő program túlterheli az add_values ​​nevű függvényt. Az első függvénydefiníció két int értéket ad hozzá. A második függvénydefiníció hozzáadja a három értéket. A fordítás során a C++ helyesen határozza meg a használandó függvényt:

#beleértve

int add_values(int a,int b)

{
return(a+b);
)

int add_values ​​(int a, int b, int c)

(
return(a+b+c);
)

{
cout<< «200 + 801 = » << add_values(200, 801) << endl;
cout<< «100 + 201 + 700 = » << add_values(100, 201, 700) << endl;
}

Amint látható, a program két függvényt definiál add_values ​​néven.Az első függvény két int értéket ad hozzá, míg a második három értéket. Nem kell konkrétan semmit tennie, hogy figyelmeztesse a fordítót a túlterhelésre, csak használja. A fordító a program által biztosított opciók alapján kitalálja, hogy melyik függvényt használja.

Hasonlóképpen, a következő MSG_OVR.CPP program túlterheli a show_message függvényt. Az első show_message nevű függvény egy szabványos üzenetet jelenít meg, nem adnak át neki paramétereket. A második a neki átadott üzenetet, a harmadik pedig két üzenetet ad ki:

#beleértve

void show_message(void)

{
cout<< «Стандартное сообщение: » << «Учимся программировать на C++» << endl;
}

void show_message(char *message)

{
cout<< message << endl;
}

void show_message(char *első, char *második)

{
cout<< first << endl;
cout<< second << endl;
}

{
show_message();
show_message("Tanulj meg programozni C++ nyelven!");
show_message("Nincsenek előítéletek a C++-ban!", "A túlterhelés menő!");
}

AMIKOR TÚLTERHELÉS SZÜKSÉGES

A túlterhelés egyik legáltalánosabb felhasználási esete, hogy egy függvényt használunk, hogy különböző paraméterekkel meghatározott eredményt állítsunk elő. Tegyük fel például, hogy a programjában van egy day_of_week nevű függvény, amely a hét aktuális napját adja vissza (0 a vasárnap, 1 a hétfő, ..., 6 a szombat). A program túlterhelheti ezt a függvényt, így helyesen adja vissza a hét napját, ha Julian-napot ad meg paraméterként, vagy ha adott egy napot, hónapot és évet:

int_week_nap(int julian_day)

{
// Operátorok
}

int_week_nap (int hónap, int nap, int év)

{
// Operátorok
}

Ahogy a következő leckékben az objektum-orientált programozást vizsgálja C++ nyelven, a függvények túlterhelését fogja használni a programok teljesítményének növelésére.

A funkciók túlterhelése javítja a program olvashatóságát

A C++ függvény túlterhelése lehetővé teszi, hogy a programok több, azonos nevű függvényt definiáljanak. A túlterhelt függvényeknek azonos típusú* értékeket kell visszaadniuk, de a paraméterek számában és típusában eltérhetnek. A funkciótúlterhelés megjelenése előtt a C++-ban a C programozóknak több, majdnem azonos nevű függvényt kellett létrehozniuk. Sajnos az ilyen funkciókat használni kívánó programozóknak emlékezniük kellett arra, hogy melyik paraméterkombináció melyik függvénynek felel meg. Másrészt a függvénytúlterhelés leegyszerűsíti a programozók dolgát, mivel csak egy függvénynévre van szükség.* A túlterhelt függvényeknek nem kell azonos típusú értékeket visszaadniuk, mivel a fordító egyedileg azonosítja a függvényt a neve és a függvénye alapján. érvek halmaza. A fordító számára az azonos nevű, de eltérő argumentumtípusú függvények különböző függvények, így a visszatérési típus az egyes függvények kiváltsága. - kb.ford.

AMIT TUDNOD KELL

A függvénytúlterhelés lehetővé teszi több definíció megadását ugyanahhoz a funkcióhoz. A fordítás során a C++ az átadott paraméterek száma és típusa alapján határozza meg, hogy melyik függvényt használja. Ebből az oktatóanyagból megtanulta, hogy könnyű túlterhelni a funkciókat. A 14. leckében megtudhatja, hogyan teszik egyszerűvé a C++ hivatkozások a függvényeken belüli paraméterek megváltoztatását. Mielőtt azonban továbblépne a 14. leckére, győződjön meg arról, hogy megtanulta a következő alapfogalmakat:

  1. A funkció túlterhelése több „nézetet” biztosít ugyanarról a funkcióról a programon belül.
  2. A függvények túlterheléséhez egyszerűen definiáljon több, azonos nevű és visszatérési típusú függvényt, amelyek csak a paraméterek számában és típusában különböznek egymástól.
  3. A fordítás során a C++ az átadott paraméterek száma és típusa alapján határozza meg, hogy melyik függvényt hívja meg.
  4. A funkciók túlterhelése leegyszerűsíti a programozást, mivel lehetővé teszi a programozóknak, hogy csak egy függvénynévvel dolgozzanak.

Funkció túlterhelés

A műveletek túlterhelése (operátorok, funkciók, eljárások)- a programozásban - a polimorfizmus megvalósításának egyik módja, amely abban áll, hogy egy művelet (operátor, függvény vagy eljárás) több különböző változatának egy hatókörében egyidejűleg létezhetnek, amelyek azonos nevű, de a paraméterek típusaiban különböznek. amelyre alkalmazzák.

Terminológia

A "túlterhelés" kifejezés az angol "overloading" pauszpapírja, amely az 1990-es évek első felében jelent meg a programozási nyelvekről szóló könyvek orosz fordításában. Talán ez nem a legjobb fordítási lehetőség, mivel az orosz "túlterhelés" szónak megvan a maga kialakult jelentése, amely gyökeresen különbözik az újonnan javasolttól, azonban gyökeret vert és meglehetősen elterjedt. A szovjet korszak kiadványaiban a hasonló mechanizmusokat oroszul „műveletek újradefiniálásának” vagy „újradefiniálásának” nevezték, de ez a lehetőség nem vitatható: eltérések és zavarok merülnek fel az angol „override”, „overload” és „ újradefiniál".

A megjelenés okai

A legtöbb korai programozási nyelvnek megvolt az a megkötése, hogy egy programban egyszerre legfeljebb egy művelet volt elérhető azonos néven. Ennek megfelelően a program egy adott pontján látható összes függvénynek és eljárásnak más-más nevet kell viselnie. A programozási nyelv részét képező függvények, eljárások és operátorok neveit és megnevezéseit a programozó nem használhatja saját függvényeinek, eljárásainak és operátorainak megnevezésére. Egyes esetekben a programozó létrehozhat saját programobjektumot egy másik, már létező nevével, de ekkor az újonnan létrehozott objektum „átfedésbe kerül” az előzővel, és lehetetlenné válik mindkét opció egyidejű használata.

Ez a helyzet néhány meglehetősen gyakori esetben kényelmetlen.

  • Néha szükség van olyan műveletek leírására és alkalmazására a programozó által létrehozott adattípusokra, amelyek jelentésükben egyenértékűek a nyelven már elérhetőkkel. Klasszikus példa a komplex számokkal való munkavégzésre szolgáló könyvtár. A közönséges numerikus típusokhoz hasonlóan támogatják az aritmetikai műveleteket, és természetes lenne az ilyen típusú műveletekhez létrehozni a „plusz”, „mínusz”, „szorzás”, „osztás” műveletet, ugyanazokkal a műveleti előjelekkel jelölve őket, mint a többi numerikus esetében. típusok. A nyelvben definiált elemek használatának tilalma számos függvény létrehozását kényszeríti ki olyan néven, mint a ComplexPlusComplex, IntegerPlusComplex, ComplexMinusFloat és így tovább.
  • Ha azonos jelentésű műveleteket alkalmazunk különböző típusú operandusokra, akkor kénytelenek más elnevezést kapni. Ha nem lehet azonos nevű függvényeket használni a különböző típusú függvényekhez, akkor ugyanarra a dologra különböző neveket kell kitalálni, ami zavart okoz, és akár hibákhoz is vezethet. Például a klasszikus C nyelvben a szabványos könyvtárfüggvénynek két változata létezik egy szám modulusának meghatározására: abs() és fabs() – az első egész argumentumhoz, a második valós argumentumhoz való. Ez a helyzet a gyenge C típusú ellenőrzéssel kombinálva nehezen megtalálható hibához vezethet: ha egy programozó abs(x)-et ír a számításba, ahol x egy valós változó, akkor néhány fordító figyelmeztetés nélkül kódot generál, ami alakítsuk át x-et egész számmá a tört részek elvetésével, és számítsuk ki a modulust a kapott egészből!

Részben a problémát objektumprogramozással oldják meg – amikor új adattípusokat osztályokként deklarálunk, a rajtuk végzett műveletek osztálymetódusokká formalizálhatók, beleértve az azonos nevű osztálymetódusokat is (mivel a különböző osztályok metódusainak nem kell rendelkezniük különböző nevek), de egyrészt a különböző típusú értékekre vonatkozó műveletek ilyen tervezési módja kényelmetlen, másrészt nem oldja meg az új operátorok létrehozásának problémáját.

A kezelői túlterhelés önmagában csak "szintaktikai cukor", bár még önmagában is hasznos lehet, mert lehetővé teszi a fejlesztő számára, hogy természetesebb módon programozzon, és az egyedi típusok jobban viselkedjenek, mint a beépítettek. Ha általánosabb oldalról közelítjük meg a kérdést, akkor azt láthatjuk, hogy azok az eszközök, amelyek lehetővé teszik a nyelv bővítését, új műveletekkel, szintaktikai konstrukciókkal történő kiegészítését (és a műveletek túlterhelése az objektumok, makrók mellett az egyik ilyen eszköz , funkcionalitások, lezárások) már a metanyelvben is megfordítják – a konkrét feladatokra orientált nyelvek leírásának eszköze. Segítségével minden konkrét feladathoz a számára legmegfelelőbb nyelvi bővítmény építhető, amely lehetővé teszi a megoldás legtermészetesebb, legérthetőbb és legegyszerűbb leírását. Például a műveletek túlterhelésére szolgáló alkalmazásban: összetett matematikai típusok (vektorok, mátrixok) könyvtárának létrehozása és a velük végzett műveletek természetes, „matematikai” formában történő leírása létrehoz egy „vektorműveletek nyelvét”, amelyben a a számítások rejtve vannak, és a feladatok megoldását vektor- és mátrixműveletekkel lehet leírni, nem a technikára, hanem a probléma lényegére koncentrálva. Ezen okok miatt kerültek egykor az Algol-68 nyelvbe ilyen eszközök.

Túlterhelési mechanizmus

Végrehajtás

Az operátorok túlterhelése két, egymással összefüggő tulajdonság bevezetését jelenti a nyelvbe: több eljárás vagy függvény deklarálása azonos néven ugyanabban a hatókörben, valamint a műveletek saját implementációinak leírása (vagyis a műveletek jelei, általában infix jelöléssel, operandusok közé írva). Alapvetően végrehajtásuk meglehetősen egyszerű:

  • Ahhoz, hogy több azonos nevű művelet létezhessen, elegendő egy olyan szabályt bevinni a nyelvbe, amely szerint egy műveletet (eljárást, függvényt vagy operátort) a fordító nemcsak névről (jelölésről) ismer fel, hanem paramétereik típusai szerint is. Tehát abs(i), ahol i egész számként van deklarálva, és abs(x), ahol x valósnak van deklarálva, két különböző művelet. Alapvetően nem okoz nehézséget egy ilyen értelmezés megadása.
  • A műveletek meghatározásához és újradefiniálásához megfelelő szintaktikai konstrukciókat kell bevezetni a nyelvbe. Elég sok lehetőség lehet, de valójában nem különböznek egymástól, elég megjegyezni, hogy az űrlap bevitele „<операнд1> <знакОперации> <операнд2>» alapvetően hasonló a « függvény meghívásához<знакОперации>(<операнд1>,<операнд2>)". Elég, ha a programozó függvények formájában leírja az operátorok viselkedését - és a leírás problémája megoldódik.

Lehetőségek és problémák

Az eljárások és funkciók általános elképzelés szintjén történő túlterhelése általában nem nehéz sem megvalósítani, sem megérteni. Azonban még ebben is van néhány "csapdája", amelyeket figyelembe kell venni. Az operátor túlterhelésének engedélyezése sokkal több problémát okoz mind a nyelvi implementátornak, mind az ezen a nyelven dolgozó programozónak.

Azonosítási probléma

Az eljárások és funkciók túlterhelését lehetővé tévő nyelvi fordító fejlesztője előtt az első kérdés a következő: hogyan lehet kiválasztani az azonos nevű eljárások közül azt, amelyiket ebben az esetben alkalmazni kell? Minden rendben van, ha van az eljárásnak olyan változata, amelynek formális paramétereinek típusai pontosan megegyeznek a jelen hívásban használt tényleges paraméterek típusaival. Azonban szinte minden nyelvben van bizonyos fokú szabadság a típusok használatában, feltételezve, hogy a fordító bizonyos helyzetekben automatikusan típusbiztos konverziót hajt végre. Például a valós és egész argumentumokkal végzett aritmetikai műveleteknél az egész szám általában automatikusan valós típusúvá alakul, és az eredmény valós. Tegyük fel, hogy az add függvénynek két változata van:

int add(int a1, int a2); float add(float a1, float a2);

Hogyan kezelje a fordító az y = add(x, i) kifejezést, ahol x egy float, i pedig int? Nyilvánvaló, hogy nincs pontos egyezés. Két lehetőség van: vagy y=add_int((int)x,i) , vagy y=add_flt(x, (float)i) (itt az add_int és add_float nevek a függvény első és második verzióját jelölik) .

Felmerül a kérdés: engedélyeznie kell-e a fordítóprogramnak a túlterhelt függvények használatát, és ha igen, mi alapján választja ki az adott változatot? A fenti példában különösen a fordítónak figyelembe kell vennie az y változó típusát a választáskor? Megjegyzendő, hogy a fenti helyzet a legegyszerűbb, ennél sokkal bonyolultabb esetek is lehetségesek, amit súlyosbít, hogy nem csak a beépített típusok konvertálhatók a nyelv szabályai szerint, hanem a programozó által deklarált osztályok is. , ha rokoni kapcsolataik vannak, egymásnak vethetők. A problémára két megoldás létezik:

  • Egyáltalán tiltsák a pontatlan azonosítást. Követelje meg, hogy minden egyes típuspárhoz legyen egy pontosan megfelelő változata a túlterhelt eljárásnak vagy műveletnek. Ha nincs ilyen lehetőség, akkor a fordítónak hibát kell dobnia. A programozónak ebben az esetben explicit konverziót kell alkalmaznia, hogy a tényleges paramétereket a kívánt típuskészletre öntse. Ez a megközelítés kényelmetlen az olyan nyelvekben, mint például a C++, amelyek meglehetősen nagy szabadságot engednek meg a típusok kezelésében, mivel jelentős különbségekhez vezet a beépített és túlterhelt operátorok viselkedésében (az aritmetikai műveletek a közönséges számokra is alkalmazhatók gondolkodás nélkül, hanem más típusokhoz - csak kifejezett konverzióval) vagy a műveletek számos lehetőségének megjelenéséhez.
  • Határozzon meg bizonyos szabályokat a „legközelebbi illeszkedés” kiválasztásához. Általában ennél a változatnál a fordító a változatok közül azokat választja ki, amelyek hívásai csak biztonságos (nem veszteséges információs) típusú konverziókkal érhetők el a forrásból, és ha több is van belőlük, akkor az alapján választhat, hogy melyik változat igényel kevesebbet. ilyen átalakítások. Ha az eredmény egynél több lehetőséget hagy, a fordító hibát dob, és megköveteli a programozótól, hogy kifejezetten adja meg a változatot.

A működés túlterhelése specifikus problémák

Az eljárásoktól és függvényektől eltérően a programozási nyelvek infix műveleteinek két további tulajdonsága van, amelyek jelentősen befolyásolják a funkcionalitásukat: a prioritás és az asszociativitás, amelyek jelenléte az operátorok "lánc" rögzítésének lehetőségéből adódik (hogyan kell megérteni a + b * c: mint (a+b)*c vagy mint a+(b*c) Az a-b+c kifejezés jelentése (a-b)+c vagy a-(b+c) ?).

A nyelvbe épített műveletek mindig előre meghatározott hagyományos elsőbbséggel és asszociativitással rendelkeznek. Felmerül a kérdés: milyen prioritásokkal és asszociativitással bírnak majd ezeknek a műveleteknek az újradefiniált változatai, vagy ráadásul a programozó által létrehozott új műveletek? Vannak más finomságok, amelyek pontosítást igényelhetnek. Például a C-ben a ++ és -- - előtag és utótag kétféle növelési és csökkentési operátor létezik, amelyek eltérően viselkednek. Hogyan viselkedjenek az ilyen operátorok túlterhelt verziói?

A különböző nyelvek eltérő módon kezelik ezeket a kérdéseket. Így a C++-ban az operátorok túlterhelt verzióinak prioritása és asszociativitása ugyanaz marad, mint a nyelvben meghatározottak; lehetőség van a növelő és csökkentő operátorok elő- és utótag-formái külön-külön túlterhelésére speciális aláírások segítségével:

Tehát az int az aláírások változására szolgál

Új műveletek bejelentése

Az új műveletek bejelentésével még bonyolultabb a helyzet. Egy ilyen nyilatkozat lehetőségének a nyelvbe foglalása nem nehéz, de megvalósítása jelentős nehézségekkel jár. Az új művelet deklarálása tulajdonképpen egy új programozási nyelvi kulcsszó létrehozása, amit az a tény bonyolít, hogy a szöveges műveletek általában határolók nélkül követhetnek más tokeneket. Amikor megjelennek, további nehézségek merülnek fel a lexikális elemző megszervezésében. Például, ha a nyelv már rendelkezik a „+” és az unáris „-” műveletekkel (jelváltás), akkor az a+-b kifejezés pontosan értelmezhető + (-b)-ként, de ha egy új művelet +- deklarálva van a programban, azonnal kétértelműség keletkezik, mert ugyanaz a kifejezés már a (+-) b -ként is értelmezhető. A nyelv fejlesztőjének és megvalósítójának valamilyen módon meg kell küzdenie az ilyen problémákkal. A lehetőségek szintén eltérőek lehetnek: megkövetelik, hogy minden új művelet egykarakteres legyen, feltételezzük, hogy eltérés esetén a művelet „leghosszabb” verzióját választjuk (vagyis a következő karakterkészletig, amelyet a a fordító bármely művelethez illeszkedik, továbbra is olvasható), próbálja meg észlelni az ütközéseket a fordítás során, és hibákat generálni vitatott esetekben ... Így vagy úgy, az új műveletek deklarálását lehetővé tevő nyelvek megoldják ezeket a problémákat.

Nem szabad elfelejteni, hogy az új műveleteknél az asszociativitás és a prioritás meghatározása is felmerül. Már nincs kész megoldás szabványos nyelvi művelet formájában, és általában csak be kell állítani ezeket a paramétereket a nyelv szabályaival. Például tegyen minden új műveletet balra asszociatívvá, és adja meg nekik ugyanazt, rögzített prioritást, vagy vigye be a nyelvbe mindkettő megadásának eszközét.

Túlterhelés és polimorf változók

Ha túlterhelt operátorokat, függvényeket és eljárásokat használnak erősen tipizált nyelvekben, ahol minden változónak van előre deklarált típusa, akkor a fordítónak kell eldöntenie, hogy a túlterhelt operátor melyik verzióját használja az adott esetben, bármilyen bonyolult is legyen. . Ez azt jelenti, hogy a lefordított nyelveknél az operátor túlterhelés alkalmazása nem vezet teljesítményromláshoz - mindenesetre a program objektumkódjában van egy jól definiált művelet vagy függvényhívás. Más a helyzet, ha lehetséges polimorf változók használata a nyelvben, vagyis olyan változók, amelyek különböző időpontokban különböző típusú értékeket tartalmazhatnak.

Mivel az érték típusa, amelyre a túlterhelt műveletet alkalmazni kell, a kód fordításakor nem ismert, a fordító megfosztja attól a képességétől, hogy előzetesen válassza ki a megfelelő opciót. Ebben az esetben kénytelen beágyazni egy töredéket az objektumkódba, amely közvetlenül a művelet végrehajtása előtt meghatározza az argumentumokban szereplő értékek típusát, és dinamikusan kiválasztja a típuskészletnek megfelelő változatot. Sőt, ezt a definíciót a művelet minden egyes végrehajtásakor meg kell adni, mert akár ugyanazt a kódot másodszor is meghívva máshogyan lehet végrehajtani.

Így az operátor-túlterhelés polimorf változókkal kombinálva elkerülhetetlenné teszi a meghívandó kód dinamikus meghatározását.

Kritika

A túlterhelés alkalmazását nem minden szakértő tekinti áldásnak. Ha a funkciók és eljárások túlterhelése általában nem kifogásolható (részben azért, mert nem vezet néhány tipikus „operátori” problémához, részben azért, mert kevésbé csábító a visszaélésre), akkor az operátor túlterhelése elvileg , és bizonyos nyelvi megvalósításokban, számos programozási teoretikus és gyakorló meglehetősen súlyos kritikának van kitéve.

A kritikusok rámutatnak, hogy az azonosítás, a prioritás és az asszociativitás fent vázolt problémái gyakran szükségtelenül megnehezítik vagy természetellenesek a túlterhelt operátorok kezelését:

  • Azonosítás. Ha a nyelvnek szigorú azonosítási szabályai vannak, akkor a programozó kénytelen megjegyezni, hogy mely típuskombinációkhoz vannak túlterhelt műveletek, és ezekre manuálisan öntsön operandusokat. Ha a nyelv lehetővé teszi a "hozzávetőleges" azonosítást, soha nem lehet biztos abban, hogy egy meglehetősen bonyolult helyzetben pontosan az a műveleti változat kerül végrehajtásra, amelyre a programozó gondolt.
  • Prioritás és asszociativitás. Ha mereven vannak meghatározva, ez kényelmetlen lehet, és nem releváns a témakör szempontjából (például halmazokkal végzett műveleteknél a prioritások eltérnek az aritmetikai prioritásoktól). Ha ezeket a programozó be tudja állítani, ez további hibaforrássá válik (már csak azért is, mert egy művelet különböző változatai eltérő prioritásúak, vagy akár asszociativitással rendelkeznek).

Hogy a saját műveletek használatának kényelme mennyivel tudja felülmúlni a program irányíthatóságának romlásával járó kényelmetlenséget, az kérdés, amire nincs egyértelmű válasz.

A nyelvi megvalósítás szempontjából ugyanazok a problémák vezetnek a fordítók bonyolultságához, hatékonyságának és megbízhatóságának csökkenéséhez. És a túlterhelés polimorf változókkal együtt való alkalmazása is nyilvánvalóan lassabb, mint egy keménykódolt művelet hívása fordítás közben, és kevesebb lehetőséget biztosít az objektumkód optimalizálására. A túlterhelés megvalósításának sajátosságai különböző nyelveken külön kritikának vannak kitéve. A C++-ban tehát a kritika tárgya lehet a túlterhelt függvények nevének belső megjelenítésére vonatkozó megegyezés hiánya, ami a különböző C++ fordítók által összeállított könyvtárak szintjén inkompatibilitást okoz.

Egyes kritikusok a szoftverfejlesztés elméletének általános elvei és a valós ipari gyakorlat alapján a túlterhelés ellen emelnek szót.

  • A nyelvépítés "puritán" megközelítésének hívei, mint például Wirth vagy Hoare, egyszerűen azért ellenzik a kezelői túlterhelést, mert könnyen eltekinthető tőle. Véleményük szerint az ilyen eszközök csak bonyolítják a nyelvet és a fordítót, anélkül, hogy ennek a bonyodalomnak megfelelő további funkciókat biztosítanának. Véleményük szerint a nyelv feladat-orientált kiterjesztésének létrehozásának ötlete csak vonzónak tűnik. Valójában a nyelvi bővítőeszközök használata csak a szerzője számára teszi érthetővé a programot - aki ezt a bővítményt fejlesztette. A program sokkal nehezebbé válik a többi programozó számára, hogy megértsék és elemezzék, ami megnehezíti a karbantartást, a módosítást és a csapatfejlesztést.
  • Megjegyzendő, hogy a túlterhelés használatának lehetősége gyakran provokatív szerepet játszik: a programozók, ahol csak lehetséges, elkezdik használni, ennek eredményeként a program egyszerűsítésére és egyszerűsítésére szolgáló eszköz a program bonyolításának és zavarának oka.
  • Előfordulhat, hogy a túlterhelt operátorok nem pontosan azt teszik, amit a fajtájuk alapján elvárnak tőlük. Például az a + b általában (de nem mindig) ugyanazt jelenti, mint a b + a , de az "egy" + "kettő" különbözik a "kettő" + "egy" kifejezéstől olyan nyelveken, ahol a + operátor túlterhelt. karakterlánc összefűzés.
  • Az operátor túlterhelése környezetérzékenyebbé teszi a programtöredékeket. A kifejezésekben szereplő operandusok típusának ismerete nélkül lehetetlen megérteni, mit csinál a kifejezés, ha túlterhelt operátorokat használ. Például egy C++ programban az utasítás<< может означать и побитовый сдвиг, и вывод в поток. Выражение a << 1 возвращает результат побитового сдвига значения a на один бит влево, если a - целая переменная, но если a является выходным потоком , то же выражение выведет в этот поток строку «1» .

Osztályozás

Az alábbiakban néhány programozási nyelv osztályozása látható aszerint, hogy lehetővé teszik-e a kezelő túlterhelését, és hogy az operátorok egy előre meghatározott készletre korlátozódnak-e:

Tevékenységek Nincs túlterhelés Túlterhelés van
Korlátozott műveletsor
  • Célkitűzés-C
  • Piton
Lehetőség van új műveletek meghatározására
  • PostgreSQL
  • Lásd még

    Wikimédia Alapítvány. 2010 .

    Nézze meg, mi az a „Funkciótúlterhelés” más szótárakban:

      - (operátorok, függvények, eljárások) a programozásban a polimorfizmus megvalósításának egyik módja, amely abban áll, hogy egy hatókörben egy művelet több különböző változata (operátor, függvény vagy ... ... Wikipédia) egyidejűleg létezhet.



Hogyan érhető el a funkció túlterhelése C-ben? (tíz)

Van mód a függvény túlterhelésére C-ben? Olyan egyszerű funkciókat nézek, amiket túl lehet terhelni, pl

foo (int a) foo (char b) foo (float c , int d)

Azt hiszem, nincs közvetlen út; Megkerülő megoldásokat keresek, ha vannak.

Remélem, az alábbi kód segít megérteni a funkció túlterhelését

#beleértve #beleértve int fun(int a, ...); int main(int argc, char *argv)( fun(1,10); fun(2"questionbank"); return 0; ) int fun(int a, ...)( va_list vl; va_start(vl,a ); if(a==1) printf("%d",va_arg(vl,int)); else printf("\n%s",va_arg(vl,char *)); )

Úgy értem, úgy érted – nem, nem teheted.

A va_arg függvényt így deklarálhatjuk

void my_func(char* formátum, ...);

De át kell adnia néhány információt a változók számáról és típusairól az első argumentumban – például a printf() .

Igen tetszik.

Itt adsz egy példát:

void printA(int a)( printf("Hello world from printA: %d\n",a; ) void printB(const char *buff)( printf("Hello world from printB: %s\n",buff) ; ) #define Max_ITEMS() 6, 5, 4, 3, 2, 1, 0 #define __VA_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N #define _Num_ARGS_(... ) __VA_ARG_N(__VA_ARGS__) #define NUM_ARGS(...) (_Num_ARGS_(_0, ## __VA_ARGS__, Max_ITEMS()) - 1) #define CHECK_ARGS_MAX_LIMIT(t) if(NUM_ARGS(args) #define_CHMINKKARG_t) if(NUM_ARGS(args) #define print(x , args ...) \ CHECK_ARGS_MIN_LIMIT(1) printf("hiba");fflush(stdout); \ CHECK_ARGS_MAX_LIMIT(4) printf("hiba");fflush(stdout) ; \ (( \ if (__beépített_típusok_kompatibilis_p ((x), int)) \ printA(x, ##args); \ else \ printB (x,##args); \ )) int main(int argc, char* * argv) ( int a=0; print(a); print("hello"); return (EXIT_SUCCESS); )

0-t és hello-t ad ki a printA és a printB-ből.

Ha a fordítóprogramod gcc, és nem bánod, ha minden alkalommal manuálisan frissíted, amikor új túlterhelést adsz hozzá, akkor csinálhatsz egy makrótömeget, és a hívó fél szempontjából a kívánt eredményt kapod, nem olyan szép írni... de lehetséges

nézd meg a __builtin_types_compatibilis_p-t, majd használd olyan makrók meghatározására, amelyek

#define foo(a) \ ((__builtin_types_compatibilis_p(int, a)?foo(a):(__builtin_types_compatible_p(float, a)?foo(a):)

de igen, csúnya, csak nem

EDIT: A C1X támogatást kap a típuskifejezésekhez, amelyek így néznek ki:

#define cbrt(X) _Generic((X), long double: cbrtl, \ default: cbrt, \ float: cbrtf)(X)

Mint már említettük, a túlterhelést abban az értelemben, ahogyan te érted, a C nem támogatja. A probléma megoldásának szokásos idióma az, hogy a függvény egy címkézett uniót vesz fel. Ezt a struct paraméterrel valósítjuk meg, ahol maga a struktúra valamilyen típusú típusjelzőből, például egy enumból és különböző értéktípusok uniójából áll. Példa:

#beleértve typedef enum ( T_INT, T_FLOAT, T_CHAR, ) my_type; typedef struct ( my_type type; union ( int a; float b; char c; ) my_union; ) my_struct; void set_overload (my_struct *whatever) ( switch (bármi->type) ( T_INT eset: whatever->my_union.a = 1; break; case T_FLOAT: whatever->my_union.b = 2.0; break; case T_CHAR: whatever-> my_union.c = "3"; ) ) void printf_overload (my_struct *whatever) ( switch (bármilyen->type) ( case T_INT: printf("%d\n", whatever->my_union.a); break; case T_FLOAT : printf("%f\n", whatever->my_union.b); break; case T_CHAR: printf("%c\n", whatever->my_union.c); break; ) ) int main (int argc, char* argv) ( my_struct s; s.type=T_INT; set_overload(&s); printf_overload(&s); s.type=T_FLOAT; set_overload(&s); printf_overload(&s); s.type=T_CHAR; set_overload(&s) ; printf_overload(&s); )

Nem használhatod a C++-t, és nem használhatod az összes többi C++ funkciót ezen kívül?

Ha eddig nem volt szigorú C, akkor inkább variadic függvényeket javaslok.

A következő megközelítés hasonló a a2800276, de néhány C99 makróval:

// szükségünk van a `size_t` #include // argumentumtípusok a sum_arg_types enum elfogadásához ( SUM_LONG, SUM_ULONG, SUM_DOUBLE ); // egy argumentum tárolására szolgáló struktúra struct sum_arg ( enum sum_arg_types type; union ( long as_long; unioned long as_ulong; double as_double; ) érték; ); // egy tömb méretének meghatározása #define count(ARRAY) ((Sizeof (ARRAY))/(sizeof *(ARRAY))) // így lesz a függvényünk neve #define sum(...) _sum( count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__)) // `struct sum_arg` tömb létrehozása #define sum_args(...) ((struct sum_arg )( __VA_ARGS__ )) // inicializálók létrehozása a sum argumentumokhoz #define (ÉRTÉK) ( SUM_LONG, ( .as_long = (VALUE) ) ) #define sum_ulong(VALUE) ( SUM_ULONG, ( .as_ulong = (VALUE) ) ) #define sum_double(VALUE) ( SUM_DOUBLE, ( .as_double = (ÉRTÉK) ) ) // polimorf függvényünk long double _sum(size_t count, struct sum_arg * args) ( long double value = 0; for(size_t i = 0; i< count; ++i) { switch(args[i].type) { case SUM_LONG: value += args[i].value.as_long; break; case SUM_ULONG: value += args[i].value.as_ulong; break; case SUM_DOUBLE: value += args[i].value.as_double; break; } } return value; } // let"s see if it works #include int main() ( unsigned long foo = -1; long double value = sum(sum_long(42), sum_ulong(foo), sum_double(1e10)); printf("%Le\n", érték); return 0; )

Egyelőre az _Általános, mivel a kérdés _Általános, szabványos C (kiterjesztés nélkül) kapott támogatja a funkciók túlterhelését (és nem operátorok), köszönhetően a _Generic szónak _Generic a C11-ben. (a GCC a 4.9-es verzió óta támogatja)

(A túlterhelés nem igazán "beépült" a kérdésben bemutatott módon, de könnyen elpusztítható valami, ami így működik.)

A Generic egy fordítási idejű operátor, amely ugyanabba a családba tartozik, mint a sizeof és az _Alignof. Ezt a szabvány 6.5.1.1. szakasza írja le. Két fő paraméter kell hozzá: egy kifejezés (amely nem kerül kiértékelésre futás közben) és egy típus/kifejezés asszociációk listája, ami kicsit olyan, mint egy kapcsolóblokk. A _Generic lekéri a kifejezés általános típusát, majd „átvált” rá, hogy kiválassza a listából a típusának megfelelő végső eredménykifejezést:

Generic(1, float: 2.0, char *: "2", int: 2, alapértelmezett: get_two_object());

A fenti kifejezés 2-re értékeli ki - a vezérlőkifejezés típusa int , tehát az int-hez társított kifejezést választja ki értékként. Ezek egyike sem marad futásidőben. (Az alapértelmezett záradék kötelező: ha nem adja meg, és a típus nem egyezik, akkor fordítási hibát okoz.)

A függvénytúlterhelésnél hasznos technika, hogy a C előfeldolgozó beillesztheti, és kiválaszthatja az eredménykifejezést a vezérlő makrónak átadott argumentumok típusa alapján. Tehát (példa a C szabványból):

#define cbrt(X) _Általános((X), \ long double: cbrtl, \ default: cbrt, \ float: cbrtf \)(X)

Ez a makró úgy valósítja meg a túlterhelt cbrt műveletet, hogy átadja az argumentum típusát a makrónak, kiválasztja a megfelelő implementációs függvényt, majd átadja az eredeti makrót annak a függvénynek.

Tehát az eredeti példa megvalósításához a következőket tehetjük:

Foo_int (int a) foo_char (char b) foo_float_int (float c , int d) #define foo(_1, ...) _Generic((_1), \ int: foo_int, \ char: foo_char, \ float: _Generic(( ELSŐ(__VA_ARGS__,)), \int: foo_float_int))(_1, __VA_ARGS__) #define FIRST(A, ...) A

Ebben az esetben használhatjuk az alapértelmezett: kötést a harmadik esetre, de ez nem mutatja be, hogyan lehet kiterjeszteni az elvet több argumentumra. A végeredmény az, hogy használhatja a foo(...)-ot a kódjában anélkül, hogy (sokat) aggódna az argumentumok típusa miatt.

Bonyolultabb helyzetekben, például olyan függvényeknél, amelyek több argumentumot túlterhelnek vagy számokat módosítanak, segédprogrammakrókat használhat a statikus küldési struktúrák automatikus generálására:

void print_ii(int a, int b) ( printf("int, int\n"); ) void print_di(double a, int b) ( printf("double, int\n"); ) void print_iii(int a, int b, int c) ( printf("int, int, int\n"); ) void print_default(void) ( printf("ismeretlen argumentumok\n"); ) #define print(...) OVERLOAD(print, (__VA_ARGS__), \ (print_ii, (int, int)), \ (print_di, (double, int)), \ (print_iii, (int, int, int)) \) #define OVERLOAD_ARG_TYPES (int, double) #define OVERLOAD_FUNCTIONS (print) #include "activate-overloads.h" int main(void) ( print(44, 47); // kiírja az "int, int" parancsot print(4.4, 47); // kiírja a "double, int" print (1, 2, 3); // kiírja az "int, int, int" parancsot print(""); // kiírja az "ismeretlen argumentumokat" )

(végrehajtás itt). Némi erőfeszítéssel lecsökkentheti a hőcserélőt, hogy a beépített túlterhelés-támogatással rendelkező nyelvhez nagyon hasonlítson.

Félretéve már lehetett túlterhelni összeg argumentumokat (nem pedig típust) a C99-ben.

Vegye figyelembe, hogy a C kiértékelésének módja megmozgathat. Ez a foo_int választja ki, ha például literális karaktert próbál átadni neki, és szüksége van némi foo_int-re, ha azt szeretné, hogy a túlterhelések támogassák a string literálokat. Összességében azonban nagyon klassz.

Leushenko válasza nagyon klassz: csak a foo példa nem fordítja le a GCC-t, ami a foo(7) -n meghiúsul, beleütközik az ELSŐ makróba és a tényleges függvényhívásba ((_1, __VA_ARGS__), plusz vesszővel maradva. problémákba ütközünk, ha további túlterheléseket akarunk biztosítani, például a foo(double) .

Ezért úgy döntöttem, hogy részletesebben válaszolok erre a kérdésre, beleértve az üres túlterhelés engedélyezését (foo(void) - ami némi gondot okozott...).

Az ötlet most a következő: definiáljon egynél több általánost különböző makrókban, és válassza ki a megfelelőt az argumentumok számának megfelelően!

Az érvek száma meglehetősen egyszerű, a következő válasz alapján:

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__) #define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) #define CONCAT(X, Y) CONCAT_(X, Y) # definiálja a CONCAT_(X, Y) X ## Y

Ez jó, a SELECT_1 vagy a SELECT_2 mellett döntünk (vagy több argumentum mellett, ha akarod/szükséged van rájuk), tehát csak a megfelelő definíciókra van szükségünk:

#define SELECT_0() foo_void #define SELECT_1(_1) _Általános ((_1), \ int: foo_int, \ char: foo_char, \ double: foo_double \) #define SELECT_2(_1, _2) _Általános((_1), \ double : _Általános((_2), \ int: foo_double_int \) \)

Először is, egy üres makróhívás (foo()) továbbra is létrehoz egy tokent, de az üres. Tehát a számláló makró valójában 1-et ad vissza 0 helyett, még akkor is, ha a makrót üresnek nevezik. "Könnyen" megoldhatjuk ezt a problémát, ha __VA_ARGS__ vesszővel a __VA_ARGS__ után feltételesen, attól függően, hogy a lista üres-e vagy sem:

#define NARG(...) ARG4_(__VA_ARGS__ VESZSŐ(__VA_ARGS__) 4, 3, 2, 1, 0)

azt nézett könnyű, de a COMMA makró elég nehéz; szerencsére ezzel a témával már Jens Gustedt blogja is foglalkozik (köszönöm, Jens). A fő trükk az, hogy a függvénymakrók nem bővülnek ki, hacsak nem zárójelek követik őket, további magyarázatért lásd Jens blogját... Csak egy kicsit módosítanunk kell a makrókat az igényeinknek megfelelően (rövidebb neveket és kevesebb érvet használok a rövidség kedvéért) .

#define ARGN(...) ARGN_(__VA_ARGS__) #define ARGN_(_0, _1, _2, _3, N, ...) N #define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0 ) #define SET_COMMA(...) , #define COMMA(...) SELECT_COMMA \ (\ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_COMMA(SET_COMMA __VA_ARGS__), \_HAS_COMMA_(_SET_COMMA) \) #define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3) #define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _3 #2 ## define COMMA_0000 , #define COMMA_0001 #define COMMA_0010 , // ... (az összes többi vesszővel együtt) #define COMMA_1111 ,

És most jól vagyunk...

Teljes kód egy blokkban:

/* * demo.c * * Létrehozva: 2017-09-14 * Szerző: sboehler */ #include void foo_void(void) ( puts("void"); ) void foo_int(int c) ( printf("int: %d\n", c); ) void foo_char(char c) ( printf("char: %c \n", c); ) void foo_double(double c) ( printf("double: %.2f\n", c); ) void foo_double_int(double c, int d) ( printf("double: %.2f, int: %d\n", c, d); ) #define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__) #define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) # define CONCAT(X, Y) CONCAT_(X, Y) #define CONCAT_(X, Y) X ## Y #define SELECT_0() foo_void #define SELECT_1(_1) _Generic ((_1), \ int: foo_int, \ char : foo_char, \ double: foo_double \) #define SELECT_2(_1, _2) _Generic((_1), \ double: _Generic((_2), \ int: foo_double_int \) \) #define ARGN(...) ARGN_( __VA_ARGS__) #define ARGN_(_0, _1, _2, N, ...) N #define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0) #define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0) #define SET_COMMA(...) , #define VESZSŐ(...) SELECT_COMMA \ (\ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_C OMMA(SET_COMMA __VA_ARGS__), \ HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \) #define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3) #MA_1, SELECT3, _2 COMMA_ ## _0 ## _1 ## _2 ## _3 COMMA_1001 , #define COMMA_1010 , #define COMMA_1011 , #define COMMA_1100 , #define COMMA_1101 , #define COMMA_1101 , #define COMMA_1101 , #define COMMA_1101, #define COMMA_1111 mainc, COMMA_11110. ( foo(); foo(7); foo(10,12); foo(12,10; 7); foo((char)"s"); visszatérés 0; )

A C++ lehetővé teszi egynél több definíció megadását funkciókat név ill operátor ugyanazon a területen, mint túlterhelés funkcióés kezelői túlterhelések illetőleg.

A túlterhelt deklaráció egy olyan deklaráció, amely ugyanazzal a névvel van deklarálva, mint egy, ugyanabban a hatókörben korábban deklarált deklaráció, azzal a különbséggel, hogy mindkét deklarációnak különböző argumentumai vannak, és nyilvánvalóan más a definíciója (megvalósítása).

Ha túlterhelt telefont hív funkció vagy operátor, a fordító úgy határozza meg a legmegfelelőbb definíciót, hogy összehasonlítja a függvény vagy operátor meghívásához használt argumentumtípusokat a definíciókban megadott paramétertípusokkal. A legmegfelelőbb túlterhelt függvény vagy operátor kiválasztásának folyamatát nevezzük túlterhelési felbontás .

Funkció túlterhelés C++ nyelven

Ugyanazon függvénynévhez több definíció is tartozhat ugyanabban a hatókörben. A függvénydefinícióknak különbözniük kell egymástól az argumentumlistában szereplő argumentumok típusa és/vagy száma tekintetében. Nem lehet túlterhelni azokat a függvénydeklarációkat, amelyek csak a visszatérési típusukban különböznek egymástól.

Alább látható egy példa, ahol ugyanaz a funkció nyomtatás() különböző típusú adatok nyomtatására szolgál -

#beleértve névtér használata std; class printData ( public: void print(int i) ( cout<< "Printing int: " << i << endl; } void print(double f) { cout << "Printing float: " << f << endl; } void print(char* c) { cout << "Printing character: " << c << endl; } }; int main(void) { printData pd; // Call print to print integer pd.print(5); // Call print to print float pd.print(500.263); // Call print to print character pd.print("Hello C++"); return 0; }

Nyomtatási belső: 5 Nyomtatási lebegés: 500.263 Nyomtatási karakter: Hello C++

Kezelői túlterhelés C++ nyelven

A C++-ban elérhető legtöbb beépített operátor felülírható vagy túlterhelhető. Így a programozó a felhasználó által meghatározott típusú operátorokat is használhatja.

A túlterhelt operátorok speciális nevű függvények: az "operátor" kulcsszó, amelyet a definiálandó operátor karaktere követ. Mint minden más függvénynek, a túlterhelt operátornak is van visszatérési típusa és paraméterlistája.

Box operátor+(const Box&);

használható hozzáfűzés operátort deklarál kiegészítéseket két Box objektumot, és visszaadja a végső Box objektumot. A legtöbb túlterhelt operátor közönséges nem tag függvényként vagy osztálytag függvényként definiálható. Abban az esetben, ha a fenti függvényt nem osztálytag függvényként definiáljuk, akkor minden operandushoz két argumentumot kell átadnunk az alábbiak szerint:

Box operátor+(const Box&, const Box&);

Az alábbiakban egy példa mutatja be az operátor fogalmát, amikor egy tagfüggvénnyel töltik be. Itt egy objektumot adunk át argumentumként, amelynek tulajdonságait ezzel az objektummal lehet elérni, az objektumot, amely ezt az operátort hívja, a következővel lehet elérni ez kezelő az alábbiak szerint -

#beleértve névtér használata std; class Box ( public: double getVolume(void) ( return hossz * szélesség * magasság; ) void setLength(double len) ( long = len; ) void setBreadth(double bre) ( szélesség = bre; ) void setHeight(double hei) ( magasság = hei; ) // Túlterhelés + operátor két Box objektum hozzáadásához. Box operator+(const Box& b) ( Box box; box.length = this->length + b.length; box.breadth = this->breadth + b .breadth; box.height = this->height + b.height; return box; ) private: dupla hosszúság; // Doboz hossza dupla szélesség; // Doboz szélessége dupla magasság; // Doboz magassága ) ; // A program fő funkciója int main() ( Box1; // Box1 típusú Box Box2 deklarálása; // Box2 Box Box3 típusú doboz deklarálása; // Doboz típusú 3. doboz deklarálása double volume = 0,0; // Tárolás egy doboz térfogata itt // box 1 specifikáció Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0); // 2. doboz specifikáció Box2.setLength(12.0); ;Box2.setHeight(10.0) ); // az 1. doboz térfogata kötet = Box1.getVolume(); cout<< "Volume of Box1: " << volume <

Amikor a fenti kódot lefordítják és végrehajtják, a következő kimenetet produkálja:

1. doboz térfogata: 210 2. doboz térfogata: 1560 3. doboz térfogata: 5400

Túlterhelhető / Nem túlterhelhető Operátorok

Az alábbiakban felsoroljuk azokat az operátorokat, amelyek túlterhelhetők.



A témát folytatva:
ablakok

Natalya Komarova , 2009. 05. 28. (2018. 03. 25.) Amikor egy fórumot vagy blogot olvasol, a bejegyzések szerzőire becenévvel és ... a felhasználó képével, az úgynevezett avatárral... emlékszel.