Függvények, eljárások
Definiálás:
Visszatérési_típus
függvény_neve( formális_paraméterek ) //A függvény fejléce
(fejlécsora).
{
//A függvény törzse: ide írjuk a függvény
kódját (műveleteit)
return eredmény; //Így adjuk vissza az eredményt, befejezve a függvény
működését.
}
Visszatérési_típus: a függvény által kiszámolt eredményt itt szoktuk visszaadni.
A függvény_ neve.
A formális_paraméterek: a függvény meghívásakor itt szoktunk átadni adatokat ill. változó hivatkozásokat, amelyek felhasználása befolyásolhatja a függvény működését.
Pl: Írjunk egy függvényt, amelyik paraméterként kap két egész számot, és eredményként megadja a kettő közül a nagyobbikat. Legyen a függvény neve max, paraméterként kapjon két egész számot, ezek közül kell a nagyobbiknak az értékét visszaadni, amely szintén egy egész szám, tehát a függvény vissza térési típusa is egész lesz.
int max( int a, int b )
{
int eredmeny; //
lokális változó, csak a függvényen belül látható.
if ( a>b )
eredmény = a;
else
eredmeny = b;
return
eredmeny; //itt
ér véget a függvény működése és itt adjuk vissza az eredményt is.
}
A return hatására kilépünk a függvényből, tehát az előző példában nem szükséges bevezetnünk egy lokális változó:
int max( int a, int b )
{
if ( a>b )
return a; //az eredmény legyen a
értéke
else
return b; //az eredmény
legyen b értéke
}
Felhasználás:
A függvények felhasználását a kódban meg kell előznie a deklarálásuknak (vagy teljes definiálásuknak).
Deklarálás: A függvény fejlécsorának a megadása pontosvesszővel lezárva (a paraméterek neveit nem kell megadni csak a típusát).
Pl.: Írjunk programot, amelyik két beolvasott egész szám közül kiírja a nagyobbikat.
#include
<iostream>
using namespace std;
int
max( int, int ); //Itt deklaráljuk a függvényt.
int main()
{
int a,b;
//Beolvassuk a két számot
cout << "Kerem az egyik szamot: ";
cin >> a;
cout << "Kerem a masik szamot: ";
cin >> b;
cout << "A nagyobbik szam:
" << max(a,b) << endl; //Itt hívjuk meg a függvényt, és eredményét
kiírjuk.
return 0;
}
//Itt van a függvény
definiálása:
int max( int a, int b
)
{
if ( a>b )
return a;
else
return b;
}
Eljárás: olyan függvény, amelynek nincs visszatérési típusa. Ekkor a visszaérési típusnak a void kulcsszót kell írni.
Pl.: void eljaras( formális paraméterek );
Az eljárásnál is használhatjuk a return utasítást az eljárásból való kilépésre, csak utána pontosvesszőt írjunk és ne egy kifejezést.
void eljaras( int a, int b )
{
//Ide valamilyen tevékenység et írunk.
return; //Itt lépünk ki az eljárásból.
}
Paraméterátadás:
Kétféle paraméterátadást fogunk használni:
1. Érték szerinti paraméterátadás: a formális paraméterlistán megadott paraméterből keletkezett lokális változóba másolódik a híváskor megadott aktuális paraméter értéke. A lokális változó értékén történő bármely változtatásnak semmi kihatása sincs a híváskori aktuális paraméterváltozó értékére.
Megadása: típusnév paraméter_név
Pl: a lenti programot futtatva:
void fv( int a) //az a változó felveszi a híváskori értéket, 10-et (lásd main())
{
a = a+1; //Itt
a-t növeljük eggyel, azaz a==11
cout << a << endl; //Itt kiírjuk a 11-et
}
int main()
{
int x = 10;
fv(x); //x akt. paraméterrel hívjuk fv-t, x értéke bemásolódik a lokális a változóba
cout << x << endl; //x értéke
nem változik meg a függvényhívás során, tehát 10-et ír ki.
}
2. Hivatkozás szerinti paraméterátadás: formális paraméterlistán megadott paraméterből keletkezett lokális változóba egy hivatkozás kerül a híváskor megadott aktuális paraméterre. Tekinthetjük úgy is, hogy az aktuális paraméternek megadott változót egy másik (lokális) néven keresztül is elérhetjük. A lokális változó értékén történő változtatás a híváskori aktuális paraméter változó értékékén változtat.
Megadása: típusnév ¶méter_név
void fv( int &a) //az a
változó egy hivatkozás lesz a híváskori paraméterváltozóra
{
a = a+1; //Itt
a-t növeljük eggyel, azaz a==11
cout << a << endl; //Itt kiírjuk a 11-et
}
int main()
{
int x = 10;
fv(x); //x akt. paraméterrel hívjuk fv-t, x-re hivatkozik a lokális a
változó
cout << x << endl; //x értéke
megváltozott függvényhívás során, tehát 11-et ír ki.
}
Mikor melyiket
használjuk?
Ebben a félévben, hivatkozás szerinti paraméterátadást, akkor használjunk, ha olyan eljárást vagy függvényt írunk ahol az eredményt paraméter útján szeretnénk visszaadni, vagy azt szeretnénk, hogy az eljárás vagy függvény módosítsa a paraméterként adott változót. Különben használjunk értékszerinti paraméterátadást. (Ettől csak a tömböknél térjünk el, lásd később.)
Példák hivatkozás
szerinti paraméterátadásra:
Írjunk eljárást, amely beolvas egy egész számot. A függvény felhasználásával a több számot beolvasó kód egyszerűsíthető, mivel a beolvasó eljárást csak egyszer kell megírni.
#include
<iostream>
#include
<string>
using namespace std;
void beolvas(string szoveg, int &a); //az a változón keresztűl adjuk
vissza a beolvasott számot
int main()
{
int x,y;
beolvas( "Kerem
az egyik szamot: ", x
); //Beolvasás az x-be
hivatkozáson keresztül
beolvas( "Kerem az masik szamot: ", y );
//Beolvasás az y-ba hivatkozáson keresztül
cout << "A ket szam osszege: " << x+y
<< endl;
return 0;
}
void beolvas(string szoveg, int &a)
{
cout << szoveg; //Kiírjuk a kapott szöveget
cin >>
a; //Beolvassuk a számot
}
Írjunk eljárást, amely megcseréli két egész típusú változó tartalmát:
#include
<iostream>
#include
<string>
using namespace std;
void csere( int &a, int &b );
void beolvas(string szoveg, int &a); //az a valtozóban
adjuk vissza a beolvasott szamot
int main()
{
int x,y;
beolvas( "Kerem
az egyik szamot: ", x ); //Beolvasás az x-be hivatkozáson keresztül
beolvas( "Kerem az masik szamot: ", y ); //Beolvasás az y-ba hivatkozáson
keresztül
csere(x,y); //itt
cseléjük fel a két változó tartalmát.
cout << "x: " << x << endl;
cout << "y: " << y << endl;
return 0;
}
void csere( int &a, int &b )
{
int temp = a;
a = b;
b = temp;
}
void beolvas(string szoveg, int &a)
{
cout << szoveg; //Kiírjuk a kapott szöveget
cin >>
a; //Beolvassuk a számot
}
Tömbök használata
függvényekben:
A tömb paraméterek „hivatkozás” szerint adódnak át. Mivel a tömb nem „tudja” a méretét, ezért a méretét külön paraméterben adjuk át. A tömb paramétereket a következő módokon használjuk:
v tömb típusú paraméter hivatkozás szerinti paraméter átadással (még ha nem is jelöljük),db-t is hivatkozás szerint adjuk át
Pl.: void beolvas( int v[], int &db );
v tömb típusú paraméter hivatkozás szerinti paraméter átadással (még ha nem is jelöljük),db-t érték szerint adjuk át
Pl.: void rendez( int v[], int db);
v tömb típusú paraméter hivatkozás szerinti paraméter átadással (még ha nem is jelöljük), de a const jelző miatt v elemit nem tudjuk változtatni, db-t érték szerint adjuk át
Pl.: void kiir( const int v[], int db);
Tehát a tömböknél nem kell használni a hivatkozás & jelet, mégis hivatkozás szerint adódnak át. Ha a tömböt const-ként adjuk át, akkor a tartalmát nem tudjuk változtatni (a tömb változón keresztül).
Függvény visszatérési értéke:
A return után megadott kifejezéssel lehet egy visszatérési értéket megadni.
Pl.: egy egészet visszaadó függvény:
int FV(
paraméterek )
{
…
return 5;
}
Ha több értéket is szeretnénk visszaadni, azt összetett típussal tudjuk megtenni:
struct eredmeny{
int x,y;
};
eredmeny FV( paraméterek )
{
eredmeny e;
e.x
= 1;
e.y
= 0;
return e;
}
Ebben félévben ne használjunk a visszatérési típusoknál módosítókat (pl. referencia).
Egy példa függvények használatára:
Feladat: Adjuk meg egy egynél nagyobb természetes szám prímtényezős felbontását.
Specifikáció:
Be: x: N
Ki: v: Sorozat(N) //természetes számokból álló sorozat, indextartománya legyen [1..n]
Ef: x>1
Uf:
A programban definiáljunk 3 függvényt: a beolvasásra, a prímtényezős felbontásra és az eredmény kiírására.
A feladat specifikációjában és a megoldásban a prímtényezők tárolására egy sorozatot használtunk. Az implementációban ez a sorozat legyen egészekből álló tömb.
A tömböt két változóval tudjuk reprezentálni, az egyik „maga a tömb” (előre beállítva egy max méretre), a másik a tömb elemeinek a száma.
int v[MAX_DB]; int db;
Megoldandó probléma a sorozat használt műveleteinek a megadása a tömbre:
v := ÜresSorozat - üressé teszi v-t (kiüríti)
Egyszerűen a tömb elemeinek számát állítsuk 0-ra
db
= 0;
v.Hozzáfűz(i) – ami a v sorozathoz hozzáfűzi i-t
Írjuk be a tömb következő szabad helyére i-t és a tömb elemeinek a számát növeljük 1-gyel:
v[db]
= i;
db
= db +1;
ez rövidebben:
v[ db++ ] = i;
//-------------------------------------------------------------------------
#include
<iostream>
using namespace std;
const int MAX_DB = 100; //a tömb elemeinek max száma, ennyi helyet foglalunk le
void beolvas(int &x);
void felbontas(int x, int v[], int &db);
void eredmenyt_kiir(int x, const int v[], int db);
int main()
{
int v[MAX_DB];
int db;
int x;
beolvas(x);
//beolvassunk x változóba
felbontas(x,v,db); //elvégezzük x prímtényezős felbontását
v-be
eredmenyt_kiir(x,v,db); //kiirjuk a primtényezőket
return 0;
}
//Beolvassa az
egynél nagyobb természetes számot
void beolvas(int &x)
{
do
{
cout << "Kerem az 1-nel nagyobb termeszetes
szamot: ";
cin >>
x;
} while( x<2 ); //akkor
ér véget a ciklus, ha x>=2
}
//x primtényezős felbontása, v tömbbe lesznek a prímek
void felbontas(int x, int v[], int &db)
{
db = 0;
int i=2;
while( i<=x )
{
if( x%i ==
0 )
{
v[ db++ ]
= i;
x/=i;
}
else
++i;
}
}
//kiirjuk az eredményt
void eredmenyt_kiir(int x, const int v[], int db)
{
cout << x << " felbontasa:
" << endl;
for( int i=0; i<db; ++i)
cout << v[i] << endl;
}
/-------------------------------------------------------------------------
Gyakorlásképpen a korábbi gyakorlatok feladatait oldjuk meg függvények/eljárások felhasználásával. A programot tagoljuk legalább három különálló részre:
- input adatok beolvasása
- feladat megoldása
- eredmények kiírása
Ezeket a feladattól függően tovább lehet bontani kisebb eljárásokra/függvényekre.
Megjegyzések:
- A függvény nevének és a hozzá tartozó paramétereknek együtt kell egyedinek lenni, azaz definiálhatunk azonos nevű függvényeket, különböző paramétertípusokkal ill. paraméterszámmal.
Pl.: csere függvény különböző
típusokra:
void csere( int &a, int &b);
void csere( string &a, string &b);
- A visszatérési típus nem számít a függvény egyediségénél. Tehát azonos nevű és paraméterű, de különböző visszatérési értékű függvényeket is azonosnak látja a fordító, hibát jelez.
- Ne használjunk pointereket (paraméterátadáshoz, visszatérési típusnál), ebben a félévben nincs szükség a pointerekre.
- Ne használjunk referenciát visszatérési típusként, a lokális változókra való hivatkozási hibák elkerülésére, inkább referencia paramétert használjunk az eredmény visszaadására.
Feladatok: