Programming | C / C++ » Bevezetés az információs technológiákba

Datasheet

Year, pagecount:2003, 39 page(s)

Language:Hungarian

Downloads:196

Uploaded:November 07, 2010

Size:277 KB

Institution:
-

Comments:

Attachment:-

Download in PDF:Please log in!



Comments

11110 Anonymus April 4, 2014
  Lektorálni nem vagyok hivatott.. a tankönyveimet juttatja eszembe ;P

Content extract

http://www.doksihu Bevezetés az információs technológiákba Fejlesztői környezetek: • GNU C++ • MS Visual C++ • Watcom C++ • Turbo C • Borland C++ (free) (free) (conio.h nem használható; 16 bites) (conio.h nem használható) Bevezető:  a C++ a C nyelvből származik  ’70-es évek Bell laboratórium, Unix operációs rendszer fejlesztése: az operációs rendszer újraírásához új, magas szintű nyelvre van szükség. A rendelkezésre álló magas szintű nyelvek (COBOL, FORTRAN, PL1,) nem eredményeztek hatékony kódot (bináris kódot)  Új nyelv és fordító Dennis M. Ritchie nevéhez fűződik: C nyelv (Dennis M. Ritchie, Brian W Kennighan: A C programozási nyelv)  ’80-as évek közepén kezd terjedni az objektum orientált programozás (OOP); Bjourn Stronstup (AT&T) felismerte az objektum orientált programozásnak, mint módszernek a jelentőségét. Ettől kezdve a C nyelvet ilyen irányban fejlesztik tovább  A nyelv mai

formáját ANSI ajánlások határozzák meg.  A C++ kicsi, strukturált nyelv (kevesebb, mint 50 kulcsszót tartalmaz), de ezzel szemben a programnyelvek közül a legtöbb operátort tartalmazza. A C++ nyelvi elemek: • Folyamok (streamek) használata; Előre definiált típus #include <iostream.h> //Borland & Turbo C esetén streamh int main (){ int n; cout<<”Enter an integer:”; cin>>n; cout<<”The value:”<<n<<’ ’; //Egyszerűen újabb elemet fűzhetünk hozzá return 0; } • Konstansok és konstans paraméterek szintaxis: const típus változó[=érték] const double PI=3.1415926536; 1/38 http://www.doksihu A const változó értékét nem változtathatjuk meg; előnye, hogy ismeri a fordító a típusát (define-nal ellentétben). • Logikai típus (ANSI C++ ’96-tól) Szintaxisa: bool változó // a változó lehetséges értékei: true, false #include <iostream.h> //cin és cout miatt bool Prime(const

int n){ int i; if (n<2) return false; for (i=2;i*i<=n;i++) if (n%i==0) return false; return true; } int main (){ int n; cout<<”Enter an integer:”; cin>>n; if (Prime(n)) cout<<”Prime. ”; else cout<<”Non prime. ”; return 0; } • Azonos nevű függvények többféle paraméterrel vagy argumentummal #include <iostream.h> int Abs(const int i){ //const mert i értékét nem akarjuk megváltoztatni return i<0?-i:i; } double Abs(const double d){ return d<0.0?-d:d; } int main(){ int n; double f; cout<<”Enter an integer number:”; cin>>n; cout<<”Enter a real number:”; cin>>f; cout<<”The absolute value of the integer:” <<Abs(n)<<’ ’; cout<<”The absolute value of the real number:” 2/38 http://www.doksihu <<Abs(f)<<’ ’; return 0; } Az azonos nevű függvények közül hogyan választ a fordító: • az argumentumok száma: hívható-e a függvény adott

számú paraméterrel? • az argumentumok típusa: kevesebb típuskonverzióra van-e szükség, mint más, azonos nevű függvény hívása esetén Az előző példa alapján: 1. feltétel alapján nem lehet eldönteni, mert ugyan annyi az argumentum 2. feltétel alapján hajtódik végre a program Alapértelmezett argumentumérték - C++-ban egy függvény argumentumában szereplő változóknak adhatunk alapértelmezett (default) értéket, - Függvény deklarációjában inicializált argumentum (default értékkel rendelkező) paraméter után csak inicializált paraméter következhet (pl.: (int b, int c, int a=1,) - Ha egy függvénynek n db argumentuma van, és ebből m db-nak alapértelmezett (default) értéke, akkor a függvény meghívható n-m db, n-m+1,.,n számú paraméterrel. Tehát legalább a nem inicializáltakat meg kell adni! Pl.: #include <iostrem.h> void test (int a, int b=2, int c=3){ cout<<a<<b<<c; } int main( ){ test(1); // 1 2 3 fog

kiíródni test(2,1) // 2 1 3 fog kiíródni test(3,2,1) // 3 2 1 fog kiíródni return 0; } • A new és delete operátorok, 0 mutató new = memóriafoglalás Szintaxisa: mutató neve = new típus Pl.: int *p; p=new int[10] //10 egészet tartalmazó tömb címének lefoglalása 0 mutató = a pointerhez nem tartozik memóriacím: sehova sem mutató mutató. Ha egy mutató értéke 0, akkor nem tárterületre mutat, hanem azt jelzi, hogy nem foglaltunk hozzá memóriát. delete : memória felszabadítása szintaxisa: delete mutató neve; 3/38 http://www.doksihu Pl.: • delete p; Cím szerinti paraméterátadás Szintaxisa: típus & változó Pl.: int & i; Példaprogram: #include <iostream.h> void GetInt(int &n,const int lb=0,const int ub=100) //adott intervallumba eső szám bekérése { bool ok; do{ cin>>n; if (n<lb) cout<<"The required integer value must be greater or equal to "<<lb<<"."; if (n>ub)

cout<<"The required integer value must be less or equal to "<<ub<<"."; ok = (n>=lb && n<=ub); if (!ok) cout <<" Re-enter the value please: "; }while (!ok); } void GetNumbers(int* &t,int &num) //egy egész típusú tömb bekérése { int i; cout<<"Enter the number of the values: "; GetInt(num,1,10); if (t) delete t; //ha t=0, vagyis hamis, akkor törölje t=new double[num]; for (i=0;i<num;i++){ cout<<"Enter the "<<i+1<<". value: "; cin>>t[i]; } } void PutNumbers(const int* t,const int num) //A const használata • (Paraméter) Változók : o const int i; : i tartalma nem változtatható o const int *a; : az ’a’ mutató által mutatott tárterület pl.: tömb nem változtatható o int *const a; : az ’a’ mutató nem változtatható meg o const int *const b; : sem a ’b’ mutató, sem az általa mutatott tárterület nem változtatható meg

4/38 http://www.doksihu { int i; for (i=0;i<num;i++) cout<<"The "<<i+1<<". value: "<<t[i]<<" "; } double Min(const int* t,const int num) //tömb minimális elemének kiválasztása { int i; int min=t[0]; //inicializálás az 1. elemmel for(i=1;i<num;i++) if (t[i]<min) min=t[i]; return min; } double Avg(const int* t,const int num) //a tömb átlagának meghatározása { int i; double sum=t[0]; for(i=1;i<num;i++) sum += t[i]; return sum/double(num); } int main( ){ int *a=0; int n; GetNumbers(a,n); PutNumbers(a,n); cout<<”The smallest number is: ”<<min(a,n)<<” ”; cout<<”The average of the array is: “<<Avg(a,n)<<” ”; if(a) delete a; return 0; } Paraméterváltozók a memóriában: • Érték szerint átadott paraméterváltozó: az átadott változó tartalma lemásolódik egy másik tárterületre void test1(int b){ b=3; } //2.2 int main(){ int a=1; test1(a);

return 0; } //2.1 //1. //2. //3. 5/38 http://www.doksihu 1. a | | 0 0 0 0 0 0 Cím: E0000001 0 2 b | | 0 0 0 0 0 0 0 2 Cím: E0000001 2.2 b | | 0 0 0 0 0 0 0 2 Cím: E0000001 2.1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 3 0 0 0 1 3. a | | 0 0 0 0 0 0 Cím: E0000001 • 0 2 Változó érték szerint átadott címe: void test2(int *p){ //2.1 *p=3; //2.2 } int main(){ int a=1; //1. test2(&a); //2. return 0; //3. } 1. a | | 0 0 0 0 0 0 0 1 Cím: E0000001 2.1 *p | | | 0 0 0 0 0 Cím: E0000001 p 0 | 0 1 E 0 0 0 6/38 http://www.doksihu 2.2 *p | | | 0 0 0 0 0 Cím: E0000001 P 0 | 0 3 E 0 0 0 0 0 0 1 3. a | | 0 0 0 0 0 0 E0000001 0 3 Cím-szerinti paraméterátadás vagy referencia típusú paraméter esetén egy új

névvel, az eredeti tárterületre hivatkozunk void test3(int &b){ b=3; } int main(){ int a=1; test3(a); return 0; //2.1 //2.2 //1. //2. //3. } 1. a | | 0 0 0 0 0 Cím: E0000001 2.1 b | | 0 0 0 0 0 Cím: E0000001 2.2 b | | 0 0 0 0 0 Cím: E0000001 3. a | | 0 0 0 0 0 Cím: E0000001 0 0 1 0 0 1 0 0 3 0 0 3 7/38 http://www.doksihu Mikor melyiket használjuk? Paraméterátadás optimalizálása: 1. Változik a paraméterváltozó értéke 1.1 Az átadott változó értékét szeretnénk megőrizni: érték szerinti paraméterátadás (1 eset) 1.2 Vissza szeretnénk kapni a megváltozott értéket: cím szerinti paraméterátadás (3 eset) 2. A paraméterváltozó értéke nem változik 2.1 változó mérete <= a címének méreténél : érték szerint átadott konstans paraméter Pl.: (const int b); 2.2 változó mérete > a címének méreténél: cím szerinti

átadás konstans paraméterbe vagy konstans referencia Pl.: (const string &s); Megjegyzés: jobbérték (rvalue) cím szerint csak konstans paraméterben adható át. Pl.: void Print( const string &s){ } Print(string(”123b”)+s1+’ ’+”.”); Akkor érdemes a címet átadni, ha a cím kisebb helyet foglal, mint a változó értéke (pl. stringek). Hogyan működik a fordító: Forráskódok Tárgykódok (object) állományok (source code) fordítás *.obj ; *.o (Executable) *.c ; *.h Függvénykönyvtárak(library) könyvtárak *.cpp ; *.hpp compeling *.lib ; *.a link libraries) (platform függetlenek) (CPU függő) 1.h 1.cpp ->1obj 2.h 2.cpp -> 2 obj 3.h 3.cpp -> 3obj Futtatható összekapcsolás Dinamikus linking (Dynamic *.exe, *.dll (Op. rendszer függő) 12.lib 123.exe (pontosan egy cpp-ben szerepeljen main függvény) OBJEKTUM-ORIENTÁLT PROGRAMOZÁS (OOP) (Object-oriented programming) Közelíti a természetes gondolkodást: - bizonyos

kijelentések/megfogalmazások csak adott kontextusban értelmesek - eszközhasználat annak belső szerkezetét nem ismerve 8/38 http://www.doksihu Mi az OOP? Az OOP nem programozás (kódolás), hanem programtervezési, szervezési módszertan. A programok szervezéséről szól: szervezzük a programjainkat objektumok köré. Az objektum állapotváltozók + viselkedések. A viselkedések olyan műveletek, amelyek hozzáférnek az állapotokhoz, vagy megváltoztathatják azokat. Egységbezárás (encapsulation) Az OOP támogatja az egységbezárást. Az egyetlen mód egy objektum állapotának megváltoztatására saját műveletein keresztül. Egy objektum belseje (állapotok) a külvilág számára rejtett. Nincs olyan OOP nyelv, mely az egységbezárást ne támogatná Az egységbezárás hogyan változtatja meg a szoftverfejlesztést? - a kapcsolódó felületek tervezésével kell több időt tölteni  próbáljuk a felületet minél pontosabban meghatározni  a

felület megváltoztatása „fájdalmas” - a szoftver életciklusa átalakul a korábbi programozási feladatokhoz képest A szoftverfejlesztés egyes fázisaira fordított idő százalékban hagyományos és OO programozási módszerrel. Tervezés Kódolás és hibakeresés Tesztelés Hagyományos 30-40% 35-45% 15-20% OOP 70% 20% 10% Az egységbezárás előnyei: • Könnyű karbantarthatóság: nincsenek belső összefüggések a kódrészek között • Plug & Play: azonos viselkedésű objektumok egymással helyettesíthetők (különböző implementáció) • Könnyebb érthetőség: csak a felületet kell érteni • Gyors fejlesztés: egyszerű megvalósítás később kifinomultabbra cserélhető; csak az objektum belsejében kell változtatni • Moduláris építkezés: minden objektum egy modul, ami a többitől függetlenül fejleszthető/tesztelhető Megjegyzés: az OOP három legfontosabb jellemzője: - egységbezárás - öröklés/származtatás -

többalakúság / poliformizmus Ezen lehetőségeket a későbbiekben, ebben a sorrendben tárgyaljuk. Osztályok és objektumok Analógia: típus -> változó osztály -> objektum osztály : összetett adatszerkezet a hozzá kapcsolódó műveletekkel együtt objektum : egy konkrét példány az adott típuson belül 9/38 http://www.doksihu Osztály deklarációja Szintaxis: class osztálynév{ Adattagok; Műveletek (függvények formájában); }; Objektum deklarációja Szintaxis: osztálynév objektumnév; Dinamikusan: osztálynév *objektummutató; Objektummutató = new osztálynév; Objektum törlése = delete objektummutató; Pl.: class class1{ }; int main(){ class1 obj,*pobj; pobj=new class1; delete pobj; return 0; } • //*pobj dinamikus objektum Osztály metódus: class store{ int s; public: int GetStire() const(); Ha egy metódus konstans típus akkor az osztályban tárolt adattagokat nem változtatja meg. • Konstans objektumoknak csak konstans típusú

metódusai hívhatóak. // Egységbezárás: Hozzáférési jogosultságok módosításával adatok és eljárások/függvények (metódusok) egy része a külvilág számára nem hozzáférhető (private) más része nyilvános (public). Alapértelmezés szerint minden private. Pl.: class cl1{ private: int state; public: void SetState(const int i); int GetState(); }; Pl.: void cl1::SetState(const int i){ 10/38 http://www.doksihu state=i; } int cl1::GetState(){ return state; } int main(){ cl1 o; int j; o.SetState(10); //ostate=10;<-sérti a jogosultságot j=o.GetState(); return 0; } Konstruktor és destruktor: 1. Konstruktor 1.1 Olyan tagfüggvény, melynek neve megegyezik az osztályéval 1.2 Nem rendelkezik visszatérési értékkel 1.3 Akkor indul el az eljárás, amikor az osztályba tartozó objektum létrejön 1.4 Feladata az állapotváltozók inicializálása ( !dinamikus változók, adatszerkezetek) 2. Destruktor 2.1 Olyan tagfüggvény melynek neve: ~osztálynév 2.2

Nincs visszatérési értéke 2.3 Akkor indul el, amikor egy, az osztályba tartozó objektum megszűnik 2.4 Feladata az osztályban tárolt dinamikus adatszerkezetek törlése Pl.: class cl2{ public: cl2(); ~cl2(); }; cl2::cl2(){}; cl2::~cl2(){}; int main(){ cl2 o,*po; po=new cl2; delete po; return 0; } //o.cl2() lefut //po->cl2() //po->~cl2() //o.~cl2() lefut Paraméterezett és alapértelmezett (default) konstruktorok • • • A konstruktoroknak más metódusokhoz hasonlóan lehetnek argumentumaik. Ekkor paraméterezett konstruktorról beszélünk. Ha egy konstruktornak nincs argumentuma, akkor alapértelmezett konstruktorról beszélünk. A konstruktor argumentumait egy objektum létrehozásakor adhatjuk meg. 11/38 http://www.doksihu Szintaxis: <osztálynév> <objektumnév> (konstr. paraméterei) vagy Objektummutató = new osztály név (konstr. Paraméterei) • Ha egy objektum létrehozásakor nem adunk meg paramétereket a konstruktor számára, akkor

a default konstruktor indul el. Tipikus példa: objektumtömb létrehozása Szintaxis: <osztálynév> <osztálytömb>[objektumok száma] Ekkor a tömb minden elemére a default konstruktor hívódik meg. Pl.: #include <iostrem.h> class cl2{ private: int state; public: cl2(){state=0;}; cl2(const int pstate) {state = pstate ;}; //e két sor helyett írható a köv.: (const int pstate=0){state=pstate;};// int GetState(){return state;}; }; int main(){ cl2 a,*b,c; //a.cl2() ≡ astate = 0 cl2 d(2); //d.cl2(2) ≡ dstate = 2 b=new cl2; //b->cl2() ≡ b->state = 0 c=new cl2(3); //c->cl2(3) ≡ c->state = 3 cout<<a.GetState()<<b->GetState()<<c->GetState() <<d.GetState; // 0 0 3 2 ír ki delete b; delete c; return 0; } Beágyazott függvények (inline functions) Amennyiben egy metódus törzsét beleírjuk az osztály deklarációjába, akkor ezt a metódust a fordító beágyazott függvényként fogja kezelni. Beágyazott

függvény esetén nincs paraméterátadás és függvény hívás, hanem a fordító a beágyazott függvény törzsét bemásolja a hívás helyére. Beágyazott függvény deklarációjának szintaxisa: inline <fv deklarációja> Gyakran használt kódrészletek hatékony megvalósítása • Függvényírás: int Abs(const int i){ return i<0?-i:i;} 12/38 http://www.doksihu • Beágyazott függvény: inline int Abs(const int i){ return i<0?-i:i;} // be fogja másolni a hívás helyére: b=Abs(b); esetén i=b; b=(i<0?-i:i); előnye: nincs függvényhíváshoz kapcsolódó adminisztráció, tehát gyorsabb hátránya: sokszor hívjuk meg -> sok helyre másolódik -> nő a kód mérete • Makró használata: #define Abs(x)(x<0?-x:x) y=Abs(y); //y=(y<0?-y:y); z=Abs(z++); //z=(z++<0?-z++:z++); Itt a z értéke nem 1-gyel változik! előnye: gyors hátránya: nem várt eredménnyel szolgálhat Dinamikus adatszerkezetek : verem Műveletek: • push:

egy elem berakása a verembe • pop: egy elem kivétele a veremből A vermet, mint adatszerkezetet LIFO-nak is hívják (Last In First Out). Amit utoljára tettünk a verembe, azt vehetjük ki először. Példa: 5 pus h 2 268 2 pus h 5 5 2 a=8 pop befelé 2 -> pus h 6 6 2 pus h 8 8 6 2 pop a b= 6 6 2 pop b a=2 2 pop a 8 6 2 kifelé A verem megfordítja a benne tárolt adatok sorrendjét. Verem megvalósítása dinamikus tömbben tárolva. //stack1.h #ifndef STACK H #define STACK H 13/38 http://www.doksihu const int DEFAULT STACK SIZE=5; class stack{ private: int maxelemnum,elemnum,*store; bool Full(); public: stack(int pmaxelemnum=DEFAULT STACK SIZE); bool Empty(); bool Push(const int n); bool Pop(int &n); ~stack(); }; #endif // STACK H //stack1.cpp #include ”stack1.h” #include <iostream.h> stack::stack(int pmaxelemnum){ maxelemnum=pmaxelemnum; store=new int[maxelemnum]; elemnum=0; } bool stack::Full(){ return elemnum==maxelemnum; } bool

stack::Empty(){ return elemnum==0; } bool stack::Push(const int n){ if (Full()) return false; store[elemnum++]=n; return true; } bool stack::Pop(int &n){ if (Empty()) return false; n=store[--elemnum]; return true; } stack::~stack(){ delete store; } int main(){ stack *s=new stack; char c; //a főprogram 14/38 http://www.doksihu int n; do{ cout<<”(P)ush (P)op (E)xit ”; cout<<”Enter your selection : ”; cin>>c; if(c==’p’){ cout<<”Enter a number:”; cin>>n; if(s->Push(n)) cout<<”OK! ”); } if(c==’o’){ if(s->Pop(n)) cout<<n<<’ ’;} else cout<<”Empty stack. ”; }while (c!=’e’); delete s; return 0; } Verem megvalósítása dinamikusan láncolt listával 2 Stack elem* 5 int Elem + köv. címe (next) 8 stack elem* X NULL pointer //stack2.h #ifndef STACK2 H #define STACK2 H struct stack elem{ int n; stack elem *next; }; class stack{ protected: stack elem *top; public: stack(); bool

Empty(); bool Push(const int n); bool Pop(int &n); ~stack(); }; #endif STACK H //stack2.cpp #include ”stack2.h” 15/38 http://www.doksihu stack::stack(){ top=0; } bool stack::Empty(){ return top==0; } bool stack::Push(const int n){ stack elem *new elem=new stack elem; if (new elem==0) return false; new elem->n=n; new elem->next=top; top=new elem; return true; } //1 //2 //3 //4 5 2 X top Push(3) //1 new elem létrejön //2 3 new elem beleteszi a hármat //3 3 new elem Mutasson oda, ahova a top top 5 2 X //4 3 5 2 X top bool stack::Pop(int &n){ stack elem * to delete; if (Empty()) return false; to delete=top; n=to delete->n; top=to delete->next; delete to delete; //1 //2 //3 //4 16/38 http://www.doksihu return true; } 5 2 X top 17/38 http://www.doksihu Pop(n) //1 to delete top 5 2 X 5 2 X //2 n=5 //3 to delete top //4 to delete X top stack::~stack(){ stack elem * to delete; while (top!=0){ to delete=top; top=to

delete->next; delete to delete; } } 2 X //destruktor Származtatás és öröklés OOP tulajdonságai: - egységbezárás - származtatás, öröklés - poliformizmus fogalma: a származtatás az az eljárás, melynek eredményeként egy osztályból egy másik osztályt kapunk, miközben annak (az ősnek) néhány tulajdonságát megtartjuk (örökítjük) a leszármaztatott (gyermek) osztályba, másokat megváltoztatunk és kiegészítünk. Ősosztály –> származtatás -> származtatott (gyermek) osztály A gyermek az ős tulajdonságait örökli, de ezek nőhetnek, csökkenhetnek és változhatnak is. 18/38 http://www.doksihu Hozzáférési jogosultságok: o private: csak az osztályon belül hozzáférhetőek (legszigorúbb) Δ protected: az osztályban és származtatott osztályaiban (gyermekeiben) hozzáférhetőek □ public: tetszőlegesen hozzáférhető Hozzáférés módosítása származtatás során: szárPrivate Protected hozzáférés az maztatás

típusa ősben private NEM private protected Hozzáprotected public férhető protected A származtatás csak szigoríthatja a hozzáférés szabájait! Public private protected public Pl.: //stack3h #ifndef STACK3 H #define STACK3 H #include ”stack2.h” class adv stack:public stack{ public: bool Top(int &n); }; #endif //stack3.cpp bool adv stack::Top(int &n){ if (Empty()) return false; n=top->n; return true; } Osztálydiagram adv stack Δ Származtatás : public     top Push Pop Empty Top  jelöli a megvalósítás helyét stack Δ top Push Pop Empt y      19/38 http://www.doksihu Polimorfizmus (többalakúság/többarcúság): Metódus átdefiniálás (method overloading): Ha a származtatott osztályban definiálunk és megvalósítottunk azonos nevű és argumentumú metódust, mint amilyen nevű és paraméterű az ősben volt, akkor: a) A származtatott osztályban és annak leszármaztatott osztályaiban, metódusaiban

az átdefiniált metódus nevével a származtatott osztálybeli metódushoz férünk hozzá. b) Az ősosztály metódusaiban változatlanul az ős osztálybeli metódushoz férünk hozzá. Pl.: #include <iostream.h> class clA{ private: void fv1(){cout<<”clA::fv1 ”;}; public void fv2(){cout<<”clA::fv2 ”;}; void fv3(){cout<<”clA::fv3 ”;}; }; class clB:public clA{ public: void fv1(){cout<<”clB::fv1 ”;}; void fv2(){cout<<”clB::fv2 ”;}; }; int main(){ clA a,*pa; clB b,*pb; a.fv2(); a.fv3(); pa=&a pa->fv2(); pa->fv3(); b.fv1(); b.fv2(); b.fv3(); pb=&b; pb->fv1(); pb->fv2(); pb->fv3(); pa=&b pa->fv1(); pa->fv2(); pa->fv3(); clB □ □ Fv1 Fv2   //a.fv1 nem férhető hozzá //clA::fv2 mert private //clA::fv3 //pa->fv1(); nem hajtható végre mert private //clA::fv2 //clA::fv3 //clB::fv1 //clB::fv2 //clA::fv3 //clB::fv1 //clB::fv2 //clA::fv3 //nem hajtható végre //clA::fv2 //clA::fv3

Szárm.: public clA ο □ Fv1 Fv2   20/38 http://www.doksihu □ □ Fv3  Fv3 a b clB □ □ □ pb=&b Fv1 Fv2 Fv3   ο □ □ pa=&b clA Fv1 Fv2 Fv3    b Megjegyzés: ősosztályra mutató pointer megkaphatja egy származtatott osztálybeli objektum címét, de csak azokhoz a metódusokhoz férünk hozzá e pointeren keresztül, melyek az ős osztályban definiáltak. Virtuális metódusok: Ha a származtatott osztályban definiálunk és megvalósítunk azonos nevű és argumentumú virtuális metódust, mint amilyen nevű és argumentumú az ősben létezett, akkor : - a származtatott osztály metódusaiban, - annak leszármazottaiban és az ősosztálytól örökölt metódusokban is az átdefiniált metódus nevével a származtatottbeli változatot érjük el. Pl.: #include <iostream.h> class clA{ private: void fv1(){cout<<”clA::fv1 ”;}; public: virtual void fv2(){cout<<”clA::fv2 ”;}; void

fv3(){cout<<”clA::fv3 ”;fv2();}; }; class clB:public clA{ public: void fv1(){cout<<”clB::fv1 ”;}; virtual void fv2(){cout<<”clB::fv2 ”;}; }; int main(){ clA a,*pa; clB b,*pb; a.fv2(); a.fv3(); pa=&a; pa->fv2(); pa->fv3(); b.fv1(); b.fv2(); //a.fv1() nem érhető el(private) //clA::fv2 //clA::fv3 //pa->fv1()nem érhető el(private) //clA::fv2 //clA::fv3 //clB::fv1 //clB::fv2 21/38 http://www.doksihu b.fv3(); pb=&b; pb->fv1(); pb->fv2(); pb->fv3(); pa=&b; pa->fv2(); pa->fv3(); return 0; } clA ο v. fv1 fv2 fv3 • • • //clA::fv3 // clB::fv1 // clB::fv2 // clA::fv3 clB::fv2 //pa->fv1()nem érhető el(private) //clB::fv2 //clA::fv3 clB::fv2 virtuális fv. meghívja clB v. fv1 fv2 fv3 • • clB::fv2 virtualitás miatt pb clA ο v. fv1 fv2 fv3 • • • pa=&b b Kétféle számoló eljárás közös őssel és módosított (átdefiniált) metódussal: // calc.cpp #include <iostream.h>

class calc{ protected: double a,b; double Get(); virtual double Result(); public: void GetInput(); void PutResult(); }; double calc::Get(){ double d; cout<<"Enter a number: "; cin>>d; return d; } void calc::GetInput(){ a=Get(); b=Get(); } void calc::PutResult(){ 22/38 http://www.doksihu cout<<"The result is: "<<Result()<< ; } double calc::Result(){ return 0.0; } class add calc:public calc{ virtual double Result(); }; double add calc::Result(){ return a+b; } class sub calc:public calc{ virtual double Result(); }; double sub calc::Result(){ return a-b; } int main(){ calc *c0; char c; do{ cout<<"(s)um (d)ifference Enter your selection: "; cin>>c; }while (c!=s && c!=d); if (c==s) c0=new add calc; else c0=new sub calc; c0->GetInput(); c0->PutResult(); delete c0; return 0; } add calc Δ • calc (ős) Δ Get GetInput PutResult v.Δ Result • • • • c0: erre hivatkozunk, de ez hívódik meg

23/38 meghívás v.Δ Get GetInput PutResult Result Szárm: public    Virtuális metódusok http://www.doksihu Bináris keresőfa Bináris fa: körmentes, irányított (erdő), összefüggő gráf ≡ fa. A csúcsok Kifoka (kiinduló élek száma) legfeljebb kettő, ezért bináris. 24/38 http://www.doksihu Példa: gyökér gyökérhez tartozó bal oldali részgráf levelek Amelyik csúcs befoka nulla, azt gyökérnek, amelyik csúcs kifoka nulla, azt levélnek nevezzük. Bináris fában egy csúcsból kiinduló két élet bal és jobb ágnak nevezzük Egy csúcshoz a bal (jobb) ágán kapcsolódó összefüggő részgráfot bal (jobb) oldali részgráfnak nevezzük. Fa bejárási algoritmusok: • PreOrder : közép(csúcs), bal(ág), jobb(ág) • InOrder : bal(ág), közép(csúcs), jobb(ág) • PostOrder: bal(ág), Jobb(ág), közép(csúcs) 1 2 4 3 5 6 7 Bejárása: PreOrder: InOrder: PostOrder: 1-2-4-3-5-6-7 4-2-1-5-3-6-7 4-2-5-7-6-3-1 Bináris

keresőfa: • dinamikus adatszerkezet • a tárolt adatokon rendezést feltételezünk • egy csúcshoz tartozó bal oldali részgráfban a rendezés szerint csak nála kisebb vagy egyenlő csúcsok vannak • egy csúcshoz tartozó jobb oldali részgráfban csak nála nagyobb elemek vannak (a rendezés szerint) 25/38 http://www.doksihu Példa.: bináris keresőfa felépítése a 4,9,7,6,2,8,10,10 számok tárolására 4 2 9 7 6 InOrder bejárással kiolvasva : 10 8 10 2,4,6,7,8,9,10,10 • Bináris keresőfa felépítése: elemek egyenkénti beszúrásával: Eljárás Beszúr (fa, elem); Ha a fa nem létezik, akkor a fa=elemet tartalmazó egyetlen csúcs; Különben Ha fa.gyökér >= elem, akkor beszúr (fabalág, elem); Különben Beszúr (fa.jobbág, elem); Eljárás vége; • Fa lebontása PostOrder eljárással ( a csúcsot tüntetjük el utoljára). Eljárás lebont (fa); Ha létezik fa.balág, akkor lebont (fabalág); Ha létezik fa.jobbág, akkor lebont

(fajobbág); Töröl fa; Eljárás vége; Miért hatékony adatszerkezet? Egy csúcs megtalálása legfeljebb pár lépésben történik, ahol a m a bináris magasság. A bináris keresőfában egy elem megtalálásához szükséges lépésszám = l 0<= l <=m A bináris fa magassága: log 2 n <= m <= n-1 ahol n az elemek száma (csúcsok száma) legjobb eset: 0<= l <= log 2 n legrosszabb eset: 0<= l <= n-1 várható értékben: l ≈ log 2 n Rendezés bináris keresőfával: Bemenet : elemek sorozata Kimenet : elemek rendezett sorozata Lépés : a) elemek berakása bináris keresőfába b) elemek kiolvasása InOrder eljárással 26/38 http://www.doksihu Rendezés bináris keresőfával: (C++ megvalósítása) //Binary Tree Sort #include <iostream.h> class tree{ private: int key; tree *less,greater; public: tree(const int i); void Insert(const int i); void GetContent(int* t,int &c); ~tree(); }; tree::tree(const int i){ key=i; less=0; greater=0; }

void TreeInsert(tree* &t,const int i){ if (t) t->Insert(i); //ha létezik a fa beszúrja i-t else t=new tree(i); //különben létrehozza } tree::~tree(){ if (less) delete less; if (greater) delete greater; } void tree::Insert(const int i){ if (i<=key) TreeInsert(less,i); else TreeInsert(greater,i); } void tree::GetContent(int* t,int &c){ if (less) less->GetContent(t,c); t[c++]=key; if (greater) greater->GetContent(t,c); } int main(){ int i,*out,c=0; char ch; tree* t=0; do{ cout<<"Enter the number: "; cin>>i; 27/38 http://www.doksihu c++; TreeInsert(t,i); do{ cout<<"Is there any other number ? (y/n): "; cin>>ch; }while (ch!=y && ch!=n); }while (ch!=n); out=new int[c]; c=0; t->GetContent(out,c); cout<<"Numbers in decreasing order: "; while (c) cout<<out[--c]<< ; delete t; return 0; } Lépések: (példa) 0) t=0 1) az ’5’ szám beszúrása key=5 tree* less=0 greater=0 2) a

’2’ szám beszúrása: key less greater=0 tree; out[1]=5, c=2 key=2 less=0 greater=0 out[0]=2; c=1 3) számok kiolvasása: b) lépés t-> GetContent: meghívja a: a) t->less->GetContent, out[0]=2, c=1 out[1]=5, c=2 4) számok kiírása: out[1]->5 out[0]->2 Másoló konstruktor (copy constructor): A másoló konstruktor pontosan egy argumentummal rendelkezik, amely argumentum típusa az adott osztályba tartozó objektumra hivatkozó referencia típus, általában konstans. 28/38 http://www.doksihu Pl.: class sx( sx(const sx &psx); Feladata: a paraméterként kapott objektummal azonos tartalmú példányt (objektumot) hozzon létre. Pl.: sx sx1(), sx2(sx1),*sx3; sx3=new sx(sx1); Mikor van szükség másoló konstruktorra: • Érték szerinti paraméter átadás során változó/objektum másolatát a fordító a másoló konstruktor segítségével készíti el. Pl.: void StrLen(const string s) függvény hívásakor a string::string(const

string&); konstruktor hívódik meg. • Érték szerinti visszatérési érték esetén: Pl.: string UpperCase(const string &s){ string s1; : return s1;} //e sor hatására is a string::string(const string&) másoló konstruktor hívódik meg. Cím szerinti (hivatkozás típusú) visszatérési érték A referencián keresztül közvetlenül azt a változót adja vissza, mely a return után szerepel. FONTOS! Ez a változó nem lehet az eljárás lokális változója. Pl.: class store{ private: int s; public: int &GetState(){return s;}; Ekkor a másoló konstruktor nem hajtódik végre. Alapértelmezett másoló konstruktor(cc): He egy osztályban a másoló konstruktor nem definiált, akkor a fordító alapértelmezett másoló konstruktort generál, mely az osztályban tárolt minden statikus adattagot lemásol. Következmény: dinamikus adatszerkezeteket tartalmazó objektumok másolására az alapértelmezett (default) másoló konstruktor nem alkalmas, tehát a

dinamikus adatszerkezeteket tartalmazó osztály másoló konstruktorát mindig definiálni kell. 29/38 http://www.doksihu Pl.: class store{ int state : }; : store s1(2),s2(s1) s1 s2 state=2 store Pl.: state=2 def. konstr. store class stack{ top* stack elem; : }; stack st1; st1.valami; stack st2(st1); st1 st2 Top E0120001 Top E0120001 stack stack X Itt hiba van! Ha az osztály dinamikus adatszerkezetet tartalmaz, akkor az alapértelmezett másoló konstruktor csak a mutatókat másolja le, melyek így azonos tárterületre mutatnak, tehát ha az egyik adatszerkezet megváltozik vagy megszűnik, akkor a másik is. Fontos!: ha egy osztály dinamikus adatszerkezeteket tartalmaz, akkor meg kell írni a másoló konstruktort, ami azt átmásolja. Pl.: létrehozza a verem elemeit tartalmazó láncolt lista egy újabb megvalósulását, ami a lemásolttal azonos adatokat tartalmaz. Operátorok Pl.: a+b - Pl.: a=b általában van visszatérési értéke mindig van

legalább egy argumentuma hívása és deklarációja a függvényekétől szintaktikájában különbözik. // + operátor két argumentuma van: a két összeadandó szám van visszatérési érték: a két szám összege a + operátor jellemzően nem változtatja meg az argumentumait, vagyis argumentumai konstans paraméterek. //értékadás operátor 30/38 http://www.doksihu Pl.: két argumentuma van, jellemzően a jobb oldali (második) nem változik (konstans), a bal oldali (első) változik (referencia típus) visszatérési értéke is jellemzően a második argumentum értéke a= =b //logikai egyenlőség operátor - jellemzően kettő konstans argumentuma van - jellemzően logikai visszatérési értékű Operátorok definiálása függvényként: Szintaxis: <vissz.ért típusa> operator <opjele> (argumentumok) Pl.: cl operator=(cl &a,cl b); cl operator+(const cl &a, const cl &b); bool operator==(const cl &a, const cl &b); Megjegyzés: az

operátorok argumentumainak típusait tetszőlegesen választhatjuk, de az argumentumok száma nem térhet el a nyelvre szokásostól. Pl: = op.: 2 argumentum + op.: 2 argumentum ! op.: 1 argumentum ? op.: 3 argumentum Új féle operátort nem lehet létrehozni, csak átdefiniálni a már meglévőket. Operátor definiálása osztály metódusaként: Ekkor az operátort az első argumentumának tagfüggvényeként értelmezzük. Pl.: class clx{ public: clx operator=(const clx &b); clx operator+(const clx &b) const; bool operator==(const clx &b) const; }; clx a,b,c; a=b; //a.operator = (b); hívódik c=a+b; //c.operator = (aoperator+(b)); hívódik Karaktersorozatot dinamikus tömbben tároló string osztály = és + operátorokkal //str2.h #ifndef STR H #define STR H #include <iostream.h> class string{ private: char* store; int len; 31/38 http://www.doksihu void Init(); void New(const int plen); void Delete(); public: string(); ~string(); string&

operator=(const char c); string& operator=(const char* pstore); string& operator=(const string& s); string(const char c); string(const char *pstore); string(const string &s); string operator+(const string& s)const; int Len()const{return len;}; void CopyCharsTo(char* const s, const int maxlen)const; }; int StrLen(const char *pstore); inline int StrLen(const string &s); ostream& operator<<(ostream &o,const string &s); #endif // STR H //str2.cpp #include <memory.h> #include "str2.h" int StrLen(const char *pstore){ int len=0; while (pstore[len]) len++; return len; } inline int StrLen(const string &s){ return s.Len(); } void string::Init(){ len=0; store=0; } void string::New(const int plen){ Delete(); len=plen; store=new char[len]; } void string::Delete(){ if (store) delete store; } 32/38 http://www.doksihu string::string(){ Init(); } string::~string(){ Delete(); } string& string::operator=(const char c){ New(1);

store[0]=c; return *this; } string& string::operator=(const char* pstore){ memcpy(store,pstore,len); return *this; } New(StrLen(pstore)); string& string::operator=(const string& s){ New(StrLen(s)); memcpy(store,s.store,len); return *this; } string::string(const char c){ Init(); *this=c; } string::string(const char *pstore){ Init(); *this=pstore; } string::string(const string& s){ Init(); *this=s; } string string::operator+(const string& s)const{ string s2; s2.New(len+slen); if (len) memcpy(s2.store,store,len); if (s.len) memcpy(s2store+len,sstore,slen); return s2; } void string::CopyCharsTo(char* const s,const int maxlen)const{ int s.len= len<maxlen ? len : maxlen; if (s.len) memcpy(s,store,slen); s[len]=0; } 33/38 http://www.doksihu ostream& operator<<(ostream &o,const string &s){ int len=s.Len(); char* ps=new char[len+1]; s.CopyCharsTo(ps,len); o<<ps; delete ps; return o; } A << operátor átdefiniálása sztringekre

ostream& operator<<(ostream& o,const string &s){ int len=s.len(); char *ps=new char[len+1]; s.CopyCharsTo(ps,len); o<<ps; delete ps; return o; } char& string::operator[](int i){ return store[i]; //memóriacímet ad vissza } class sstring::public string{ public: sstring():string(){}; sstring(const string& ps):string(ps){}; bool operator <=(const string& ps)const; }; bool sstring::operator <=(const string& ps)const{ int i=0; while(i<Len() && i<ps.Len() && (*this)[i]==ps[i]) i++; if(i>=Len()) return true; if(i>=ps.len()) return false; return (*this)[i]<=ps[i]; } int main(){ string s(”123”),s2,s3=s2=s; string s4(”13”); s3=s3+”abcd”; //implicit típuskonverzió s3[2]=’c’; cout<<”(”<<s3+”ABC”+’D’<<”)”<<s[1]; cout<<’ ’<<s4<<”<=”<<s; if(s4<=s) cout<<”igaz ”; else cout<<”nem igaz ”; return 0; } 34/38

http://www.doksihu Típuskonverzió • C++-ban a típuskonverzió mindig konstruktorokon keresztül valósul meg. • Egy B típusú (osztályba tartozó) változó (objektum) akkor konvertálható A típusúvá (osztályba tartozóvá), ha az A osztályban szerepel B argumentumú konstruktor. (B->A típuskonverzió) Pl.: class A{ public: A (const &B) }; Gyakorlati példa: char char* string sstring a nyilak konvertálást jeleznek • Pl.: Explicit konverzió: - a forráskódban kódrészlet hívja az A osztály B argumentumú konstruktorát a B->A típuskonverzió megvalósítására - célja olyan függvény vagy operator használatának lehetővé tétele, amely B argumentummal nem, de A argumentummal értelmezett. void test(A){}; { test (A (B) ); } //konstruktor - a típuskonverzió eredményeként egy A típusú átmeneti változó (objektum) jön létre • Implicit típuskonverzió: A fordító típuskonverziót végez, ha egy függvényt vagy operatort,

mely: a) B argumentummal nem, de A argumentummal értelmezett b) adott nevű függvényt vagy operatort B argumentummal hívjuk c) létezik A-nak B argumentumú konstruktora Pl.: test(B) //B-vel hívjuk Gyakorlati példa: void test(const sstring &s){}; int main(){ string s; sstring ss; test(ss); test(sstring(s)); //explicit: string->sstring konv. test(s); //így is jó; impicit: string->sstring konv. test(sstring(string(”abc”))); //explicit: char*->sting //explicit: string->sstring konv. test(string(”abc”)); //explicit: char*->sting //implicit: string->sstring konv. test(ssting(”abc”)); //implicit: char*->sting 35/38 http://www.doksihu //explicit: string->sstring konv. Megjegyzés: csak egy mélységig lehet implicit típuskonverziót végrehajtani! Pl.: test(”abc”); //2 mélységű implicit konverzióra a fordító nem képes! Ha lenne a string-nek char* típusú konstruktora, akkor ez is működne. Megjegyzés: explicit konverzió

tetszőleges mélységben történhet. Sablonok (template-ek) használata: • Célja : Műveletek értelmezése előre nem definiált osztályokon, azok bizonyos tulajdonságait feltételezve. Pl.: ha egy osztályon értelmezett a < és az = (legyen egyenlő) operátor, akkor sorba rendezhető. • • Előnyei: o Rövid, könnyen karbantartható kód o Univerzális kódrészleteket eredményez, mely minden, a feltételt kielégítő osztályra alkalmazható Hátrányai: o A legtöbb fordító a sablonokat csak makróként képes kezelni, ezért a függvénytörzsek nem fordíthatóak le, míg a típusok nem definiáltak. o Következmény: több forrásból felépített kód esetén a sablonnal definiált függvények törzsének is a header fájlban kell lennie. //Universal Binary-Tree Sort Feltevések typ-re: typ-nek van default konstruktora (typ :: typ( );) #include <iostream.h> template <class typ> class tree{ private: typ key; tree *less,greater; public:

tree(const typ i); void Insert(const typ i); void GetContent(typ *t , int &c); ~tree(); }; van copy konstruktora (érték szerint átadható) template <class typ> tree <typ>::tree(const typ i){ key=i; less=0; 36/38 http://www.doksihu greater=0; }; van = operátora ( typ = typ) template <class typ> void TreeInsert(tree<typ>*&t,const typ i){ if (t) t->Insert(i); else t=new tree <typ> (i); } template <class typ> void tree<typ>::Insert(const typ i){ if (i<Key) TreeInsert(less,i); else TreeInsert (greater,i); } van < operátora (typ < typ) template <class typ> void tree<typ>::GetContent(typ *t,int &c){ if (less) less->GetContent(t,c); t[c++]=key; if (greater) less->GetContent(t,c); } template <class typ> tree <typ>::~tree(){ if (less) delete less; if (greater) delete greater; } int main(){ double i,*out; int c=0; char ch; tree <double> *t=0; do{ cout<<”Enter a real

value:”; cin>>i; c++; TreeInsert(t,i); do{ cout<<”Is there other value?(y/n)”; cin>>ch; }while (ch!=’y’ && ch!=’n’); }while (ch!=’n’); out=new double[c]; c=0; t->GetContent(out,c); 37/38 http://www.doksihu c=0; cout<<”Values in decreasing order: ”; while (c) cout<<out[--c]<<’ ’; delete t; delete out; return 0; } 38/38 http://www.doksihu Ajánlott irodalom: • Stanley B. Lippmann: C++ először /Novotrade kiadó/ • Benkő Tiborné, Popper András, Benkő László: Bevezetés a Borland C++ programozásba • Dr. Kondorosi Károly, Dr Szirmai-Kalos László, Dr László Zoltán: Objektum orientált szoftverfejlsztés /Computer Book kiadó/ A jegyzetet feldolgozta: Borbély Viktor, Bertók Botond előadásai nyomán. Készült: 2003 39/38