C++ előadás és gyakorlat

Kitlei Róbert

2008/2009 tavaszi félév

Alprogram

Függvény

Előnyei

Függvény deklarációja

int f(double, char);
int g(double a, char b);

Függvény deklarációja

void e();
int e2(void);

Függvény definíciója

int f(int i, int j) {
return i + j;
}

Függvény definíciója

return utasítás

void csak_ha_i_nem_nulla(int i) {
if (0 == i) return;
...
}

Függvény definíciója

return utasítás

int f() {
if (egyszerű feltétel) return 1;
else {
// bonyolult számítás
}
}
int f() {
if (egyszerű feltétel) return 1;

// bonyolult számítás: csökkent a behúzása
}

Függvény hívása

int duplaz(int i) { return 2 * i; }

...

int i = duplaz(123);

Függvény hívása

int globalis;

int duplaz(int i) { ++globalis; return 2 * i; }

...

int i = duplaz(123) + globalis;
// i értéke nem definiált

Függvény hívása

double osszeg(double i, double j) { return i + j; }
// formális paraméterek: i, j

double d = osszeg(1., 2.) + osszeg(.3, .4);
// aktuális paraméterek
// i ⇐ 1.0, j ⇐ 2.0
// i ⇐ 0.3, j ⇐ 0.4

Függvényhívás menete

  1. tárterület lefoglalása a formális paraméterek számára
  2. a formális paraméterek kezdeti értékül kapják a megfelelő aktuális paraméterek értékét
    • szerepe hasonló a kezdeti értékadáshoz
    • típusellenőrzés
    • automatikus típuskonverzió, ha szükséges
    • osztályok objektumaira: másoló konstruktor

Függvényhívás menete

Függvényhívás menete

Függvényhívás menete

Függvényhívás menete

Függvényhívás menete

Paraméterátadás módja

Függvényhívás menete

Paraméterátadás módja

Függvényhívás menete

Paraméterátadás módja

int f(int& n) { ++n; return n; }
int  i = 734;
int& ir = i;
f(ir);
// i tartalma 735 lesz
// f visszatérési értékét nem használjuk fel

Függvényhívás menete

Paraméterátadás hivatkozás szerint

struct Nagy { int x[10000]; };

void mindet_novel(Nagy& n) {
for (int i = 0; i < 10000; ++i) ++n.x[i];
}

Függvényhívás menete

Paraméterátadás hivatkozás szerint

// az objektum kétszer is lemásolódik
Nagy nem_hatekony(Nagy n) { return n; }

// az objektum egyszer sem másolódik le
// csak a rámutató referencia
const Nagy& sokkal_hatekonyabb(const Nagy& n) {
return n;
}

Függvényhívás menete

Paraméterátadás során az adatáramlás iránya

Függvényhívás menete

int& melyiket(bool b, int& i, int& j) {
return b ? i : j;
}

...

int a, b;
int &ar = a, &br = b;
melyiket( true, ar, br) = 1; // a = 1;
melyiket(false, ar, br) = 2; // b = 2;

Függvényhívás menete

int* f();
// tegyük fel, hogy valahol meg is van valósítva

...

f()[4] = 1;
// a visszakapott tömb ötödik eleme
// felveszi az 1 értéket

Alapértelmezett paraméterek

int f(int a, int b = 7, double c = -33.3);
// helytelen
int f(int = 2, int, double = -33.3);
int f(char*=0);     // *=

Rekurzív függvényhívás

int fib(int n) {
if (0 == n) return 1;
if (1 == n) return 1;
return fib(n-1) + fib(n-2);
}

Rekurzív függvényhívás

Rekurzív függvényhívás

int fib(int n)
{
if (0 == n) return 1;
if (1 == n) return 1;
return
fib(n-1) +
fib(n-2);
}

Nézzük végig a fib(3) hívás menetét.

Rekurzív függvényhívás

int fib(int n)                  // fib(3) hívás
{
if (0 == n) return 1; // nem ez az ág
if (1 == n) return 1; // nem ez az ág
return
fib(n-1) +
// újra meghívjuk: fib(2)
fib(n-2);
}

Rekurzív függvényhívás

int fib(int n)                  // fib(2) hívás
{
if (0 == n) return 1; // nem ez az ág
if (1 == n) return 1; // nem ez az ág
return
fib(n-1) +
// újra meghívjuk: fib(1)
// fib(3) itt áll
fib(n-2);
}

Rekurzív függvényhívás

int fib(int n)                  // fib(1) hívás
{
if (0 == n) return 1; // nem ez az ág
if (1 == n) return 1; // visszatér: 1
return
fib(n-1) +
// fib(2) és fib(3) itt áll
fib(n-2);
}

Rekurzív függvényhívás

int fib(int n)                  // fib(2) hívás
{
if (0 == n) return 1;
if (1 == n) return 1;
return
fib(n-1) + // fib(3) itt áll
fib(n-2);
// fib(2) továbbmegy
// a kif. eddigi értéke: 1
// meghívja: f(0)
}

Rekurzív függvényhívás

int fib(int n)                  // fib(0) hívás
{
if (0 == n) return 1; // visszatér: 1
if (1 == n) return 1;
return
fib(n-1) + // fib(3) itt áll
fib(n-2);
// fib(2) itt áll
// a kif. eddigi értéke: 1
}

Rekurzív függvényhívás

int fib(int n)                  // fib(2) hívás
{
if (0 == n) return 1;
if (0 == n) return 1;
if (1 == n) return 1;
return
fib(n-1) + // fib(3) itt áll
fib(n-2);
// fib(2) visszatér: 1+1
}

Rekurzív függvényhívás

int fib(int n)                  // fib(3) hívás
{
if (0 == n) return 1;
if (1 == n) return 1;
return
fib(n-1) +
fib(n-2);
// fib(3) továbbmegy
// a kif. eddigi értéke: 2
// meghívja: f(1)
}

Rekurzív függvényhívás

int fib(int n)                  // fib(1) hívás
{
if (0 == n) return 1; // nem ez az ág
if (1 == n) return 1; // visszatér: 1
return
fib(n-1) +
fib(n-2);
// fib(3) itt áll
// a kif. eddigi értéke: 2
}

Rekurzív függvényhívás

int fib(int n)                  // fib(3) hívás
{
if (0 == n) return 1;
if (1 == n) return 1;
return
fib(n-1) +
fib(n-2);
// fib(3) visszatér: 2+1
}

Rekurzív függvényhívás

A fib(3) hívási fája:

Főprogram

Lehetséges szignatúrái

int main();
int main(char** argv, int argc);
int main(char** argv, int argc, char** envp);

Főprogram

Inline

inline: javaslat a függvény kódjának beillesztésére a hívások helyein

Függvények túlterhelése (overloading)

Függvények túlterhelése (overloading)

Függvények túlterhelése (overloading)

int pow(double, double);
int pow(int, int);

pow(1, 2); // helyes: int, int
pow(3.0, 4.0); // helyes: int, int
pow(5, 6.0);
// 3. feloldási szabály: int ↔ double
// helytelen, mert egyaránt lehetne
// pow((double)5, 6.0);
// pow(5, (int)6.0);

Függvényre hivatkozó mutatók

void (*f)(int, string);
(*f)(3, "abc");
f(3, "abc");
// a nyelv definíciója ezt is megengedi
// ugyanúgy hívódik meg, mint a fenti

Függvényre hivatkozó mutatók

typedef void (*sajat_fvmut)(int, string);

Névtér

névtér: logikailag összetartozó deklarációk csoportosítása

namespace N {
int n;
void f(int);
}
N::n

Névtér

namespace N {
string s;
...
}
void N::f(int x) { ... }

Névtér

using N::n;

Névtér

using namespace std;

Névtér

namespace Lib = Precise_Library_Name_v5r824;

Kivételkezelés

Kivételkezelés

// a "szokásos lefutás"
int i = f();
int j = g();
// ...
// f és g -1 értékkel jelzi,
// ha hiba történt bennük
int i, j;
if ((i = f()) == -1)
{ // f-beli hiba kezelése
} else if ((j = g()) == -1)
{ // g-beli hiba kezelése
} else
// ...

Kivételkezelés

throw kivételpéldány;
// kivétel kiváltása (dobása)

Kivételkezelés

try {
// a kivételkezelt kódrészlet
} catch (Kivételtípus1 e) {
// egyik kivételkezelő
} catch (Kivételtípus2 e) {
// másik kivételkezelő
}

Kivételkezelés

Kivételkezelés

void f(int i) throw(int, SajatTipus);