String osztly ltrehozsa Sztring osztly ltrehozsa Feladat Ksztsnk

  • Slides: 53
Download presentation
String osztály létrehozása

String osztály létrehozása

Sztring osztály létrehozása • Feladat: Készítsünk olyan programot, amely sztringeket képes létrehozni, valamint azokkal

Sztring osztály létrehozása • Feladat: Készítsünk olyan programot, amely sztringeket képes létrehozni, valamint azokkal műveleteket végezni. Az igényelt műveletek: sztring egyes karaktereinek írása illetve olvasása, sztringek másolása, összefűzése, összehasonlítása és nyomtatása. ˙ •

Objektumokat leíró táblázat:

Objektumokat leíró táblázat:

Műveletek: tipikusan operátorokkal célszerű reprezentálni őket. • Attribútumok: a specifikáció nem ad támpontot. Régebbi

Műveletek: tipikusan operátorokkal célszerű reprezentálni őket. • Attribútumok: a specifikáció nem ad támpontot. Régebbi tapasztalatainkra támaszkodjunk! • Figyelembe veendő szempontok: tárolt karakterek száma előre nem ismert és az objektum élete során is változó. Kézenfekvő a karakterek helyét a dinamikus memóriából lefoglalni, az attribútumok között csak az adminisztrációhoz szükséges változókat tartjuk nyilván. • Ilyen változó: – karaktersorozat kezdőcíme a heap-en (pstring) – a sztring aktuális hossza (size).

 • size: redundáns adat, hiszen számolható Fennáll az inkonzisztencia veszélye. Hatékonyság miatt azonban

• size: redundáns adat, hiszen számolható Fennáll az inkonzisztencia veszélye. Hatékonyság miatt azonban célszerű!

Sztring osztály definíciója • class String { char *pstring; int size; public: String (char*

Sztring osztály definíciója • class String { char *pstring; int size; public: String (char* s = ““) { //String default konstruktor pstring = new char [ size = strlen (s) + 1 ]; strcpy(pstring, s); } ~String ( ) { delete [ ] pstring ; } char& operator [ ] (int i ) { return pstring [ i ]; } String operator+( String& s ) { char *psum = new char [size + s. size - 1 ]; strcpy (psum, pstring ); strcat (psum, s. pstring) ; String sum (psum ); delete psum; return sum; }

A konstruktor feladata • - az átadott sztring alapján a szükséges terület lefoglalása a

A konstruktor feladata • - az átadott sztring alapján a szükséges terület lefoglalása a heap-en - a sztring heap-re másolása - az adminisztrációs adattagok kitöltése A konstruktordefiníció tartalmazza az alapértelmezésű (paramétert nem igénylő konstruktort is ( alapértelmezésű argumentuma miatt) •

Destruktor feladata • A heap-en lefoglalt terület felszabadítása.

Destruktor feladata • A heap-en lefoglalt terület felszabadítása.

Az index operátor • Feladata: használatával egy karaktert lekérdezhetünk, illetve megváltoztathatunk. Fontos! Referencia típus·

Az index operátor • Feladata: használatával egy karaktert lekérdezhetünk, illetve megváltoztathatunk. Fontos! Referencia típus· a visszatérési értéke.

 • main() { char c; String s (“én string vagyok” ); c =

• main() { char c; String s (“én string vagyok” ); c = s [ 3 ]; // c=s. operator[] (3); c=pstring[3]; s[ 2 ] = ‘a’; // s. operator[] (2)=‘a’; pstring[2]=‘a’; } //delete [] pstring

Az index operátor • c= s[ 3 ]; az operátor átdefiniálás szerint ekvivalens a

Az index operátor • c= s[ 3 ]; az operátor átdefiniálás szerint ekvivalens a c=s. operator[](3) utasítással, ami viszont a String: : operator[] törzse szerint a c-hez a string[3]-t rendeli hozzá. • s[2] = ‘a’ viszont a s. operator [](2) = ‘a’-nak felel meg. Ez az értékadás bal oldalán függvényhívást jelent. C-ben nem megengedett. Nem referencia típusú visszatérési érték esetén visszaadná a sztring 2 -ik karakterének értékét. Referencia típus miatt az s. operator [](2) a pstring[2] helyettesítő neve, így értéke meg is változtatható •

Referencia típus • Amennyiben olyan függvényt írunk, amelyet balértékként (az értékadás bal oldalán) akarunk

Referencia típus • Amennyiben olyan függvényt írunk, amelyet balértékként (az értékadás bal oldalán) akarunk használni, akkor annak referenciát kell visszaadnia. Az index operátor szinte mindig referencia típusú! •

Összeadás operátor • - Ideiglenesen foglalunk egy karaktertömböt - előállítjuk az összefűzött sztringet (az

Összeadás operátor • - Ideiglenesen foglalunk egy karaktertömböt - előállítjuk az összefűzött sztringet (az ideiglenesen foglalt tömbben) - megkonstruáljuk a visszatérési érték String típusú objektumát. - az ideiglenes tömböt felszabadítjuk - visszatérünk és visszatérési értékünk a létrehozott objektum.

Problémák : értékadásnál • Tekintsük a következő programrészletet: { String s 1 (“baj”), s

Problémák : értékadásnál • Tekintsük a következő programrészletet: { String s 1 (“baj”), s 2 ("van!"); s 2 = s 1; } • Az értékadás operátor (=) az adattagokat egy az egyben másolja (bitmásolás)

Bitmásolás most nem jó! • s 2. size is 4 értékű lesz • “baj”-ra

Bitmásolás most nem jó! • s 2. size is 4 értékű lesz • “baj”-ra két mutató is mutat, míg “van”-ra egy sem. • ha s 1 változik, akkor s 2 is változik • a “baj” kezdőcímére két delete-t hajt végre a program a destruktor meghívásakor, míg a “van”-t senki sem szabadítja fel. ˙ •

Értékadó operátor átdefiniálása • class String {. . . void operator=(String& ); }; void

Értékadó operátor átdefiniálása • class String {. . . void operator=(String& ); }; void String : : operator=(String& s) { delete pstring; pstring = new char [size = s. size]; strcpy (pstring, s. pstring); }

További problémák • //nem lehet s 1=s 2=s 3; • s 1=s 2=s 3

További problémák • //nem lehet s 1=s 2=s 3; • s 1=s 2=s 3 nem megengedett Ez ekvivalens lenne a következővel: s 1. operator=(s 2. operator=(s 3)); s 1. operator= bemenő argumentuma az s 2. operator=(s 3) visszatérési értéke, ami viszont void, holott String& kellene, hogy legyen. • Megoldás: visszatérési érték megváltoztatása

Még további probléma • s=s; értékadás hibásan működik. Ekkor ugyanis az s. operator= referenciaként

Még további probléma • s=s; értékadás hibásan működik. Ekkor ugyanis az s. operator= referenciaként megkapja s-t, azaz önmagát, amelyhez tartozó heap területet felszabadítja, majd innen másol. • Megoldás lehet, hogy megvizsgáljuk, hogy a referenciaként átadott objektum címe megegyezik-e a célobjektum címével.

Javított változat • class String {. . . String& operator=(String& ); //s 1=s 2=s

Javított változat • class String {. . . String& operator=(String& ); //s 1=s 2=s 3; lehet }; String& String : : operator=(String& s) { if (this != &s ) { //s = s ; miatt delete pstring; pstring = new char [size = s. size]; strcpy (pstring, spstring); } return *this; }

Problémák 2. • Értékadással kapcsolatos problémák megoldódtak. Kivéve: ha =-et használunk valamely objektumpéldány kezdőértékkel

Problémák 2. • Értékadással kapcsolatos problémák megoldódtak. Kivéve: ha =-et használunk valamely objektumpéldány kezdőértékkel való ellátására. : { String s 1(“baj”); . . String s 2 = s 1; }

Az itt szereplő = jel nem az értékadás, hanem az inicializálás műveletét jelöli. •

Az itt szereplő = jel nem az értékadás, hanem az inicializálás műveletét jelöli. • Inicializálás alapértelmezése: bitenkénti másolás. (Ha nem lenne különbég a kettő között és az operator= mindkét esetben felülbírálja a műveletet, akkor a String s 2 = s 1 utasításból kialakuló s 2. operator=(s 1) függvényhívás azzal indulna, hogy a delete s 2. pstring utasítást hajtaná végre (Pedig a pstring még nincs is inicializálva! Az operator= fv-ben nem tudjuk eldönteni, hogy volt-e már inicializálva egy objektum, vagy sem). •

Problémák 2. • Beláttuk, - hogy jó, ha nem hívja meg az operator= függvényt

Problémák 2. • Beláttuk, - hogy jó, ha nem hívja meg az operator= függvényt - rossz, hogy az inicializálás alapértelmezése szerinti műveletet hajtja végre (bitmásolás) • Megoldás “inicializáló operátort” az ún. másoló konstruktort kell átdefiniálni. •

Másoló konstruktor • Másoló konstruktor - az osztály nevét kapja - a másoló jellegét

Másoló konstruktor • Másoló konstruktor - az osztály nevét kapja - a másoló jellegét az adja, hogy ugyanolyan típusú objektummal kívánjuk inicializálni, ezért az argumentuma is ezen osztály referenciája - egy x osztályra a másoló konstruktor x ( &x).

Másoló konstruktor A String osztály kiterjesztése másoló konstruktorral: class String {. . String (

Másoló konstruktor A String osztály kiterjesztése másoló konstruktorral: class String {. . String ( String& s ); }; String: : String ( String& s ) { pstring = new char [size = s. size ]; strcpy ( pstring, s. pstring); } •

Másoló konstruktor • Definícióban történő inicializálás (az előbb láttuk a példát) • Függvény argumentum

Másoló konstruktor • Definícióban történő inicializálás (az előbb láttuk a példát) • Függvény argumentum és visszatérési érték Érték szerinti paraméterátadás esetén az argumentumok a verem memóriába kerülnek. A verem memórián tartózkodó ideiglenes változóba másolás megfelel az inicializálatlan objektum kitöltésének. A verembe másolást a másoló konstruktor végzi el. •

s 0 vermen lévő másolatát s-et a másoló konstruktor inicializálja. • Hívás String s

s 0 vermen lévő másolatát s-et a másoló konstruktor inicializálja. • Hívás String s 0, s 1; s 1 = f (s 0); temp Hívott temp String f ( String s ) { String r; //(stack). . . return r; } // r, s

Visszatérési érték kezelése • - skalár érték esetén regisztert használnak - objektumok azonban nagyobb

Visszatérési érték kezelése • - skalár érték esetén regisztert használnak - objektumok azonban nagyobb méretűek, így gyakran a globális memóriát, a heap-et vagy a veremmemóriában lefoglalt területet kell igénybe venni. A visszatérési értéket átadó memóriaterület azonban inicializálatlan, tehát a return utasítás is csak a másoló konstruktor hívásával másolhatja be a visszatérési értéket. •

Tanulságok • A létrehozott String osztály tulajdonságai: - dinamikusan nyújtózkodó szerkezet, amely minden pillanatban

Tanulságok • A létrehozott String osztály tulajdonságai: - dinamikusan nyújtózkodó szerkezet, amely minden pillanatban csak annyit foglal el a memóriából, amennyit feltétlenül szükséges. - String típusú objektumok tetszőleges számban előállíthatók. Olyan egyszerűen használhatók, mint a beépített típusok.

Öröklés Az örököltetés szintaktikája: class származtatott osztály_név: elérési mód bázis-osztály_név { // … }

Öröklés Az örököltetés szintaktikája: class származtatott osztály_név: elérési mód bázis-osztály_név { // … }

Elérési módok a származtatásnál • public • private • protected

Elérési módok a származtatásnál • public • private • protected

A származtatás: public //Alaposztály class base { int x; public: void setx (int n

A származtatás: public //Alaposztály class base { int x; public: void setx (int n ) { x=n; } void showx() { cout <<x << ‘n’; } };

Származtatott osztály class derived : public base { int y; public: void sety (int

Származtatott osztály class derived : public base { int y; public: void sety (int n ) { y=n; } void showy() { cout <<y << ‘n’; } };

Publikus tagok elérése main() { derived ob; ob. setx (10); //eléri az alaposztály tagjait

Publikus tagok elérése main() { derived ob; ob. setx (10); //eléri az alaposztály tagjait ob. sety (20); //eléri a származtatott osztály tagjait ob. showx (); //eléri az alaposztály tagjait ob. showy (); //eléri a származtatott osztály tagjait return 0; }

A private tagok nem elérhetők //Alaposztály class base { int x; public: void setx

A private tagok nem elérhetők //Alaposztály class base { int x; public: void setx (int n ) { x=n; } void showx() { cout <<x << ‘n’; } };

private tagra nem hivatkozhatunk! class derived : public base { int y; public: void

private tagra nem hivatkozhatunk! class derived : public base { int y; public: void sety (int n ) { y=n; } void show_sum() { cout <<x+y << ‘n’; } //hiba! //x private tag void showy() { cout <<y << ‘n’; } };

A származtatás: private //Alaposztály class base { int x; public: void setx (int n

A származtatás: private //Alaposztály class base { int x; public: void setx (int n ) { x=n; } void showx() { cout <<x << ‘n’; } };

Származtatott osztály class derived : private base { int y; public: void sety (int

Származtatott osztály class derived : private base { int y; public: void sety (int n ) { y=n; } void showy() { cout <<y << ‘n’; } };

Publikus tagok elérése main() { derived ob; ob. setx (10); //hiba, ez privát a

Publikus tagok elérése main() { derived ob; ob. setx (10); //hiba, ez privát a származtatott osztályban ob. sety (20); //eléri a származtatott osztály tagjait ob. showx (); //hiba, ez privát a származtatott osztályban ob. showy (); //eléri a származtatott osztály tagjait return 0; }

Megoldás az előző példára A származtatás: private //Alaposztály class base { int x; public:

Megoldás az előző példára A származtatás: private //Alaposztály class base { int x; public: void setx (int n ) { x=n; } void showx() { cout <<x << ‘n’; } };

Származtatott osztály class derived : private base { int y; public: void setxy (int

Származtatott osztály class derived : private base { int y; public: void setxy (int n, int m ) {setx(n); y=m; } void showxy() {showx(); cout <<y << ‘n’; } };

Publikus tagok elérése main() { derived ob; ob. setxy(10, 20); ob. showxy (); return

Publikus tagok elérése main() { derived ob; ob. setxy(10, 20); ob. showxy (); return 0; } //eléri a származtatott osztály tagjait

protected tagok elérése //Alaposztály class samp { int a; protected: int b; public: int

protected tagok elérése //Alaposztály class samp { int a; protected: int b; public: int c; samp(int n, int m) { a=n; b=m; } int geta ( ) { return a; } int getb ( ) {return b; } };

Hozzáférés protected tagokhoz • • main() { //ob. b=99; ob. c=30; • • cout<<

Hozzáférés protected tagokhoz • • main() { //ob. b=99; ob. c=30; • • cout<< ob. geta() << ‘ ‘; cout<< ob. getb() << ‘ ‘<<ob. c<<‘n’; return 0; } //hiba!!! //OK.

protected tagok //Alaposztály class base { protected: int a, b; public: void setab(int n,

protected tagok //Alaposztály class base { protected: int a, b; public: void setab(int n, int m) { a=n; b=m; } };

Származtatott osztály class derived : public base { public: void setc (int n) {

Származtatott osztály class derived : public base { public: void setc (int n) { c=n; } void showabc() { cout<<a<<‘ ‘ <<b<< ‘ ‘ <<c << ‘n’; } };

Publikus tagok elérése main() { derived ob; ob. setab(1, 2); ob. setc (); Ob.

Publikus tagok elérése main() { derived ob; ob. setab(1, 2); ob. setc (); Ob. showabc(); return 0; }

Konstruktorok, destruktorok és a származtatás • A bázis osztály és a származtatott osztályok is

Konstruktorok, destruktorok és a származtatás • A bázis osztály és a származtatott osztályok is rendelkezhetnek konstruktorokkal és destruktorokkal. • A konstuktorok végrahajtásának sorrendje a származtatásnak megfelelő sorrendben történik. • A destruktorok végrehajtása a származtatás sorrendjével ellenkező sorrendben történik.

Konstruktorok paraméterei • Különböző esetek: • Alaposztálynak és származtatott osztálynak nincs paramétere • Alaposztálynak

Konstruktorok paraméterei • Különböző esetek: • Alaposztálynak és származtatott osztálynak nincs paramétere • Alaposztálynak nincs paramétere • Származtatott osztálynak nincs, de a bázis osztálynak van paramétere • Bázis osztálynak is és a származtatott osztálynak is van paramétere.

Bázis és származtatott osztály konstruktorainak végrehajtási sorrendje #include <stdio. h> class base { public:

Bázis és származtatott osztály konstruktorainak végrehajtási sorrendje #include <stdio. h> class base { public: base { cout << „Bázis osztály konstruktoran”; } ~base { cout << „Bázis osztály destruktoran”; } }; Class derived: public base { public; derived { cout << „Származtatott osztály konstruktoran”; n ~derived { cout << „Származtatott osztály destruktoran”; }

A konstruktorok és destruktorok lefutása main () { derived o; return 0; } Output:

A konstruktorok és destruktorok lefutása main () { derived o; return 0; } Output: Bázis osztály konstruktora Származtatott osztály destruktora Bázis osztály destruktora

Paraméterek átadása #include <stdio. h> class base { int i; public: base ( int

Paraméterek átadása #include <stdio. h> class base { int i; public: base ( int n) { cout << „Bázis osztály konstruktoran”; i=n; } ~base { cout << „Bázis osztály destruktoran”; } void show() { cout <<i << ‘n’; } };

class derived: public base { int j; public; derived ( int n, int m)

class derived: public base { int j; public; derived ( int n, int m) : base(m) { cout << „Származtatott osztály konstruktoran”; j=n; } ~derived { cout << „Származtatott osztály destruktoran”; } void showj () { cout<< j << ‘n’; } };

main() { derived o(10, 20); o. showi(); o. showj(); return 0; }

main() { derived o(10, 20); o. showi(); o. showj(); return 0; }