Funkció mutatós oktatóanyagok

A funkciómutatók számos rendkívül érdekes, hatékony és elegáns programozási módszert kínálnak. Használhatja őket a switch / if-operátorok helyettesítésére, a saját késői kötés vagy visszahívás végrehajtására. Sajnos, valószínűleg összetett szintaxisa miatt a legtöbb számítógépes könyv és dokumentáció számukra nagyon ellenséges. Ha beszélnek róla, meglehetősen rövid és felületes. Kevésbé hibásak, mint a szokásos mutatók, soha nem osztja ki vagy szabadítja fel a memóriát. Csak annyit kell tenned, hogy megértsd, mi az, és tanulmányozzák a szintaxist. De ne feledje: mindig kérdezd meg magadtól, hogy valóban szükség van-e mutatóra egy függvényre. Kedves a saját későbbi kötődés megvalósítása, de a meglévő C + + struktúrák segítségével a kódot olvashatóvá és érthetőbbé teheti. A késői kötődés egyik szempontja a végrehajtás ideje: ha virtuális függvényt hívsz, a programnak meg kell határoznia, hogy melyik függvény hívjon. Ezt egy virtuális tábla (V-táblázat) teszi lehetővé, amely minden lehetséges funkciót tartalmaz. Minden hívás egy kis időt vesz igénybe, és lehet, hogy lerövidítheti az időt a funkciómutatók helyett virtuális funkciók helyett. Talán nem.

Ha azt szeretnénk, hogy hívja doit () függvény egy bizonyos ponton kiegészül a címkén, akkor csak a hívás doit () függvény a cue pont a forráskódot. Akkor lefordítani a kódot, és minden egyes alkalommal, amikor a program jön címkék, a funkció kerül meghívásra. Minden rendben. De mi van, ha nem tudom, hogy milyen időpontban kell hívni a funkciót? Mit csinálsz, ha ezt a futási időben szeretnéd megoldani? Akkor érdemes használni, az úgynevezett visszahívási (visszahívás funkció), vagy válasszon egy lehetőséget a medence lehetséges funkcióit. Azonban megoldhatja a problémát a kapcsoló utasítással is. ahol a függvényt pontosan úgy, ahogy akarja, a különböző egységek (ágak). De van még egy mód: használja a mutatót!

A következő példában megfontoljuk a négy alapvető számtani művelet egyikének elvégzését. Először is a feladat megoldható a kapcsoló utasítás használatával. Ezután bemutatja, hogyan lehet ugyanezt tenni egy funkciómutatóval. Ez csak egy példa, és a feladat olyan egyszerű, hogy feltételezem, senki sem fog használni funkció mutatót ehhez ;-)

Figyelembe véve a szintaxist, kétféle mutatót választhat a funkciókhoz: az egyikük a normál C funkciókra vagy a C ++ statikus tagfunkcióira mutat. Egy másik típus a C + + nem statikus tagfunkciókra mutat. A fő különbség az, hogy minden mutatónak a nem statikus tagfunkciókhoz rejtett argumentumra van szüksége: a mutató az osztály példányára. Mindig ne feledje: A két mutatótípus összeegyeztethetetlen egymással.

Mivel a mutató egy függvényhez nem más, mint egy változó. a szokásos módon kell meghatározni. A következő példában három funkciómutatót definiálunk: pt2Function, pt2Member és pt2ConstMember. Olyan függvényekre mutatnak, amelyek egy típusú float argumentumot és két argumentum karaktert tartalmaznak, és visszaadnak egy int-ot. A C ++ példákban azt feltételezzük, hogy a mutatóink által mutatott függvények a TMyClass (nem statikus) tagjai.

A C-ben egy funkciót egy mutató használatával hívunk egy olyan funkcióra, amelyet kifejezetten a * operátoron keresztül dereferenciáztunk. Alternatív megoldásként a funkció neve helyett egy mutatót is használhat. Két operátor van a C ++-ban. * és -> *, az osztály példányával együtt használva hívják az egyik (nem statikus) tagfunkciót. Ha a hívás egy másik tag funkción belül történik, használhatja ezt a mutatót.

Átirányíthat egy mutatót egy függvényre egy függvény bemeneti paramétereként. Ehhez például szükséged lesz arra, hogy mutatót mutasson a visszahívási funkcióhoz. Az alábbi kód mutatja, hogyan adhat át egy mutatót egy olyan függvénynek, amely visszaad egy int-ot. de vesz egy úszó és két char'a:

3.1 Bevezetés a visszahívási funkciók fogalmába

A funkciómutatók biztosítják a visszahívási funkciók fogalmát. Ha nem biztos benne, hogyan használható a funkciómutatók, akkor térjen vissza a "Bevezetés a funkciómutatókhoz" szakaszba. Megpróbálom bevinni a visszahívási függvény fogalmát a jól ismert rendezési függvénnyel (qsort). Ez a funkció a régió elemeit az egyedi rendezés szerint rendezi. A hatókör bármilyen típusú elemet tartalmazhat; a mutatónak a rendezési funkcióba átkerül az üresbe. Továbbá át kell adni az elem méretét és a régió összes elemének számát. Most a kérdés: hogyan rendezheti az elemzõ funkció az elemeket anélkül, hogy bármilyen típusú információról lenne szó? A válasz egyszerű: a függvény egy mutatót kap egy olyan összehasonlító függvényhez, amely érvénytelen mutatókat mutat a régió két elemére, összehasonlítja az elemeket, és egy int kódolású eredményt ad vissza. Így minden alkalommal, amikor a rendezési algoritmus két elem összehasonlításának eredményét igényli, csak a függvénymutatón keresztül hívja fel az összehasonlítási függvényt: visszahívást hajt végre!

3.2 Hogyan hajtok végre visszahívást C-ben?

3.3 Példa qsort használati kódja

A következő számjegyben úszó elemek sorrendje rendeződik.

3.4 Hogyan hajtom végre a Reverse Challenge egy tagfunkciót?

Az azonos módon történik a funkció C. A statikus tag funkció nem kell egy tárgy, hogy okozna, és így egy aláírás hasonló az aláírás C-függvények azonos hívási konvenciót kért érvek és visszaút típusát.

3.5 Hogyan lehet végrehajtani a hátramaradt kihívást nem statikus tagfüggvényben? A Wrapper megközelítés

Mutatókat nem statikus tag funkció eltér az egyszerű ukzateley a funkciókat C, mert át kell adni az osztály objektum egy pointer kísér. Így egy egyszerű függvénymutatók és nem statikus tagfüggvény különböző és összeegyeztethetetlen aláírás! Ha azt szeretnénk, hogy nézze meg a tagja egy adott osztály, csak akkor kell cserélni a kódot egy egyszerű mutató arra a funkcióra mutató egy tag függvény. De mit tehetünk, ha meg szeretné hívni nestatichsky tagja egyetlen osztály? Ez némileg nehéz. Statikus tagfüggvényt kell létrehozni csomagtartóként. A statikus tag funkció ugyanazt az aláírást tartalmazza, mint egy függvény a C! Akkor hozza a mutatót az objektum, amely a tag függvény a hívni kívánt, a void *, és adja oda a burkolat egy további paraméter vagy egy globális változót. Ha ön használ egy globális változót, akkor nagyon fontos, hogy győződjön meg arról, hogy mindig pont a megfelelő objektumot! Természetesen a hívási paramétereket át is adhatja egy tagfüggvénynek. Wrapper vezet érvényteleníti mutató mutató egy objektum osztály sootvetstvuyuego és felhívja a tag funkciót. Az alábbiakban két példa található.

Példa A: Egy osztályba instantiált mutatót újabb paraméterként továbbítanak

DoItA függvény valamit tárgyak osztálya TClassA, amely tartalmaz egy visszahívás. Így TClassA mutatót az osztály objektumot, és egy mutatót a statikus funkciót wrapper TClassA :: Wrapper_To_Call_Display át DoItA. Ez a csomagoló egy visszahívási funkció. Írhatsz tetszőleges más osztályokba megegyeznek TClassA és használja őket DoItA, amíg ezek biztosítják a szükséges funkciókat. Megjegyzés: Ez a megoldás hasznos lehet, ha tervezzen saját visszahívási felület. Sokkal jobb, mint a második megoldás, amely a globális változót használja.

B. példa: Egy osztályba instantiált mutatót egy globális változó tárol

A DoItB funkció valamit tesz a TClassA objektumokkal, amelyek visszahívást tartalmaznak. A TClassA :: Wrapper_To_Call_Display mutatót a DoItA felé továbbítjuk. Ez a csomagoló egy visszahívási funkció. A csomagoló a globális változót void * pt2Object használja, és pontosan átvált egy mutatóra a TClassB példányhoz. Nagyon fontos, hogy mindig beírja a globális változót, hogy megadja az osztály megfelelő példányát. Más tetszőleges osztályokat ugyanúgy írhat, mint a TClassB, és használhatja őket DoItA-val mindaddig, amíg nem biztosítják a szükséges funkciókat. Megjegyzés: ez a megoldás hasznos lehet, ha olyan visszahívási felületet használ, amelyet nem lehet megváltoztatni. Ez nem jó megoldás, mert egy globális változó használata nagyon veszélyes és súlyos hibákat okozhat.

4.1 Melyek a funktorok?

A függvények állapotban vannak. A C ++-ban egy vagy több privát taggal lehet osztályként ellátni az állapot tárolásához és egy túlterhelt operátorral () a függvény végrehajtásához. A függvények a C és C ++ függvényeket beágyazhatják. a sablonok és a polimorfizmus fogalmainak felhasználásával. Létrehozhatja a mutatók listáját az önkényes osztályok tagfunkcióihoz, és mindegyiküket ugyanazon az interfészen keresztül hívhatja fel, anélkül, hogy aggódna az osztályuk miatt, vagy pedig az osztó példányának mutatója. Csak erre a célra minden funkciónak azonos visszatérési típusra és hívási paraméterekre van szüksége. Néha a funktorok záróként ismeretesek. A függvények használatával visszahívásokat is kezdeményezhet.

4.2 Hogyan lehet végrehajtani a függvényeket?

Először is szükséged van egy TFunctor nevű alaposztályra, amely virtuális függvényt ad a hívásnak vagy egy virtuális túlterhelt operátornak (), amellyel tagfüggvényt hívhat le. Az Ön számára kedvelt, túlterhelt üzemeltető vagy funkció, mint a Call, függ Öntől. Az alaposztályból örökölheti a TSpecificFunctor sablonosztályt, amelyet egy olyan konstruktor iniciál, amely egy mutatót helyez el egy objektumhoz és egy mutatót egy tagfüggvényhez. A származtatott osztály felülbírálja a Hívás funkciót és / vagy az alaposztály operátort () a felülírt verzióban, akkor a tagfüggvényt a tárolt mutatók használatával hívja fel az objektumra és a tagfunkcióra. Ha nem biztos benne, hogyan lehet használni a funkciómutatókat, olvassa el a Bevezetés a funkciómutatókba.

4.3 Példa a függvények használatára

Az alábbi példában kétféle képzési osztályunk van, ezek a "Display" nevű függvényt szolgáltatják, amely semmit sem (nulla), és bemeneti paraméterként stringet (const char *) igényel. Két mutatót hozunk létre a TFunctoron, és inicializáljuk két mutatóval a TSpecificFunctor-ra, amely a TClassA és TClassB tagokra mutató objektumokat és mutatókat tartalmaz. Ezután egy sor függvényt használunk a megfelelő tagfunkciók hívására. A funkcióhívások végrehajtásához nem szükséges egy objektum mutatója, és nem kell többé aggódnia az órák miatt.

5.1 Bevezetés a funkciómutatókhoz

5.2 Visszahívások és funkciók

3.5. Szakasz. Példa

Ebben a példában, a mutató a konverziós összekeveri TClassA osztályú objektumot void * DoItA az átviteli függvény, majd inverz transzformációt a mutatót TClassA egy átalakító funkciót TClassA :: Wrapper_To_Call_Display (ugyanez vonatkozik a B példa). Hasonló tervek alkalmasak (mert mások nem választott), ha azt akarjuk, hogy működjön együtt funkciók, mint például pthread_create () (libpthread.so), amelyek figyelembe bemeneti paraméterként void *. Mindkét esetben ez a példa csak akkor működik megfelelően, ha a TClassA mutató valóban átkerül a DoItA-ra. De mi történik, ha véletlenül valami más kerül áthelyezésre a TClassA mutató helyett? A fordító nem ismeri fel a hibákat, semmi sem akadályozza meg Önnek, hogy pl. Az egyértelműség kedvéért enyhén megváltoztattam a példát (A.cpp példa):
  • a TClassA osztályba két típusú típusú int-a és x,
  • funkció TClassA :: A kijelzőn megjelenik az x,
  • a függvényben Callback_Using_Argument () két változót hozunk létre, az első egy TClassA osztály egyik objektuma, a másik pedig típusú int; Ezután mindkét esetben a DoItA függvényt hívják.
Nem nehéz kitalálni, hogy a DoItA funkció, miután beadta a mutatót bemenetként paraméterként, úgy működik, hogy enyhén szólva, nem úgy, ahogy várták. A TClassA :: x érték helyett egy nem meghatározott bitkészlet jelenik meg, más szóval szemetet. A probléma lehetséges megoldása a Wrapper_To_Call_Display és DoItB sablonfüggvények (exampleB.cpp). Mivel az int nem rendelkezik megfelelő szakosodással, a fordító hibaüzenetet fog megjeleníteni.

Powered by uCoz