Függvények new.gif (860 bytes)

C++-ban, csakúgy mint C-ben minden alprogram függvény, vagyis nincs lehetőségünk eljárások írására. Mivel azonban paraméterátadásnál használhatjuk az érvénytelen (void) típust is, ezért lehetőségünk van olyan függvények írására, amelyek nem adnak vissza érvényes értéket.

A C-vel ellentétben, ahol csak érték szerinti paraméterátadásra volt lehetőség, a C++-ban argumentumaink referencia típusúak is lehetnek, amely szintaktikailag lényegében megfelel a címszerinti paraméterátadásnak.

Opcionális argumentumok

Függvényeink argumentumai lehetnek opcionálisak is. Ekkor a függvény deklarációjakor alapértelmezett értéket kell megadnunk az argyumentumnak, amit a hívó fél átadhat, ha a programozó nem adott meg explicit módon paraméterértéket. Ilyenkor azonban a következő korlátozások lépnek életbe:

Függvények túlterhelése

A C++ lehetőséget ad a függvények túlterhelésére (overloading), vagyis azonos nevű, de eltérő szignatúrájú függvények definiálására. Az ilyen függvénycsoportok hivásakor, a fordító a hívás szignatúrájából határozza meg, hogy melyik függvény hívódjon meg.

A C++ az alábbi attribútumokat érti bele egy függvény szignatúrájába

Vegyük észre, hogy a szignatúrába a visszatérési érték típusa nem tartozik bele, vagyis ha két függvénydeklaráció pusztán visszatérési értékében különbözik, az nem számít túlterhelésnek, csak felüldefiniálásnak, és mint olyan legtöbbször fordítási hibát generál.

Túlterhelt függvény hívása esetén fontos, hogy a hívási szignatúrából kell egyértelműen kiderülnie, hogy melyik függvénypéldányt kell meghívni. Lehetséges például olyan függvénytúlterhelést alkalmazni, amelyben az egyik függvénypéldány szignatúrája a másikétől opcionális argumentumokban tér el (ez megengedett). Ekkor ha híváskor egy opcionális argumentumot sem adunk meg, a fordító nem tudja majd eldönteni a hívási szignatúrából, hogy melyik függvénypéldányt hívja. Ez gyakorlatilag azt jelenti, hogy az opcionális argumentumokkal nem rendelkező függvénypéldányt semmi esetre sem tudnánk meghívni.

Amennyiben egy függvényhívásnál a fordító nem talál olyan függvénypéldány amely szignatúrája megfelelne a hívási szignatúrának, megvizsgálja az összes olyan konverziós lehetőséget, amely során a nem megfelelő típusú argumentumból valamely függvénypéldány szignatúrájának megfelelő típusú argumentum állitható elő. Ebben a vizsgálatban csak az olyan utak jöhetnek szóba, amelyek standard konverzió, konverziós konstruktorok, vagy konverziós operátorok legfeljebb egyszeri alkalmazásával járhatók be.

Amennyiben egy vagy több szignatúrapéldányhoz egy vagy több konverziós út létezik, a fordító fordítási hibát generál.

Fontos megjegyezni, hogy függvénysablonok (template) template argumentumainak dedukálása során a fordító nem végez a fentihez hasonló konverziós útkeresést. Eszerint az alábbi példa nem a több konvertálási út létezésén, hanem (meglepő módon) nemdefiniált függvényhivatkozáson bukik meg:

template<typename T> T min( T a, T b );
//...
double a = 5.0;
int b = 6;
min( a, a + 1); 	// ok, min<double>(5.0,6.0)
b = min( b, a );	// hiba: min<?>(int,double) nem létezik

Az ilyen esetekben a dedukció elkerülhető a template argumentumok explicit megadásával:

b = min<int>( b, a );	// ok, min<int>( b, (int)a )

Tagfüggvények

A tagfüggvények valamely osztály nemstatikus függvényei. Ez a gyakorlatban annyit jelent, hogy explicit módon meghatározott argumentumlistájuk mellett egy további implicit paramétert, az ún. this pointert is átveszik. Ennek típusa class * const, ill. const class * const, attól függően hogy a függvénye nemmódosítónak deklaráltuk-e.

Például az alábbi függvénydeklarációk így néznek ki a valóságban:

// ezek a függvények ...
int myclass::myfunc1( int a, int b );
int myclass::myfunc2( int a ) const;
// így néznek ki valójában ...
int myclass_myfunc1( myclass * const this, int a, int b );
int myclass_myfunc2( const myclass * const this, int a );

A függvénytörzsön belül minden szimbólumhivatkozás (ha lehet) a this mutató dereferálásán keresztül értékelődik ki. Ez elsősorban azt jelenti, hogy az osztályszinten deklarált szimbólumok elsőbbséget élveznek, az azonos nevű, de osztályon kívül deklarált szimbólumokkal szemben. Ezt a viselkedést felülbírálhatjuk az scope resolution operator használatával, explicit módon megadva a szimbólum kiértékelésének kezdőpontját, pl.:

a = b
// jelentése
this->a = this->b
<class-or-namespace-name>::a = <class-or-namespace-name>::b
// jelentése nem változik meg
::a = ::b
// esetén mindkét szimbólumot az ún. globális scope-ban keressük.

Vegyük észre, hogy a const típusmódosítóval deklarált tagfüggvények, pont a fenti kiértékelési szabályok miatt a példányszimbólumokat csak olvasható módon érik el, ami szemléletesen azt jelenti, hogy az adott tagfüggvény nemmódosító, vagyis nem módosítja a saját osztálypéldányát.

Ugyanígy, ha egy osztálynak egy konstans példányával rendelkezünk (pl. nemmódosító mutató segítségével), akkor az implicit argumentumot (ebben az esetben nemmódosító this mutatót) csak a const típusmódosítóval deklarált tagfüggvényeknek tudjuk átadni. Ez szemléletesen azt jelenti, hogy konstans objektumpéldányokra csak nemmódosítható műveleteket alkalmazhatunk.

Szimbólumok szerkesztése

Mivel a C++ függvényszignatúrák lényegesen több komponenstől függnek, mint a C függvények szignatúrái, érthető, hogy az external szerkesztésű szimbólumok azonosítói is másképp generálódnak mint C-ben. Míg C-ben egyszerűen a szimbólumok belső neve elé tett underscore jellel kaptuk a szimbólumazonosítót, C++-ban a belső név mellé az egész szignatúra is bekódolódik.

Ez azt a lényeges problémát veti fel, hogy amikor egy C++ program fordításánál egy C program fejlécét értelmezzük (abból a célból, hogy később a kettőt összeszerkesszük), annak külső szerkesztésű szimbólumaira hibás hivatkozás kerül a tárgykódba. Ily módon lehetlenné válna a bináris kompatibilitás a legtöbb C++ ill C fordító között (holott azonos platformon a visszafele való kompatibilitást elvárnánk). A C++-ban ezért megjelent az extern <literal> direktíva, amelyben a <literal> helyére a nevezési konvenciót azonosító stringliterált kell írni.

Hatására a direktíva után szereplő blokk külső szerkesztésű szimbólumait a meghatározott nevezési konvenció alapján azonosítja a fordító. Jelenleg csak egy literált köt ki a C++ szabvány, és ez a "C", amely a C nyelv nevezési konvenciójára utal.

Alkalmazása:

extern "C" <deklaráció>;
// vagy
extern "C" { <deklaráció sorozat> };

Inline függvények

Sok késői C fordítóprogram adott már lehetőséget a helyben szerkesztett (ún. inline) függvények írására, ám ez csupán a C++ létezése óta szabványos. Az inline függvények lényege, hogy nem a deklaráció, hanem a hívás helyére szerkesztődnek, vagyis nem történik tényleges függvényhívás. Ennek előnye, hogy a függvényhívással járó futási költségeket (stack hely, idő) megspróroljuk, hátránya viszont hogy a függvény kódja nem egy helyen, hanem több helyen, szétterjedve a program kódjában található. Ez utóbbi miatt elsősorban rovidebb függvényeket érdemes csak inline módon deklarálni.

A fordítónak a szabvány szerint bármelyik függvényt módjában áll visszaminősíteni, mi tehát csak ajánlást fogalmazhatunk meg a fordító felé. Amennyiben egy inline függvény címét képezzük a programban, a fordító generál egy statikus függvénypéldányt is, ami utánna hagyományosan hívható pointeren keresztül is.

Egy függvényt inline-ná az inline kulcsszóval tehetünk, de ilyenkor a függvény törzsének mindenhonnal láthatónak kell lennie. Tagfüggvényeket úgy is deklarálhatunk inline-nak, ha az osztálydeklaráció során egyből megadjuk a függvénydefiníciót is.