ÚVOD DO C++ 3 TŘÍDY a objekty - POKRAČOVÁNÍ Objekt jako parametr funkce Spřátelené funkce a třídy
3. TŘÍDY a objekty - POKRAČOVÁNÍ Práce s objektem: 3.1 Přiřazování objektů ( = ) 3.2 Předávání objektu funkci 3.3 Vrácení objektu funkcí Spřátelené funkce a třídy: 3.4 Úvod do spřátelených a) funkcí b) tříd
- být stejného fyzického typu - musí mít stejná typová jména Dva objekty mohou být za určité podmínky navzájem přiřazeny objekt2 = objekt1; Při přiřazování jednoho objektu druhému je vytvářena bitová kopie všech datových členů. Platí, že obsah všech dat objektu1 je překopírován do ekvivalentních členů objektu2. Podmínka přiřazení: Objekty musí - být stejného fyzického typu - musí mít stejná typová jména Při nesplnění této podmínky kompilátor ohlásí chybu.
Příklad přiřazení objektů: #include <iostream>: using namespace std; class moje_trida { int a, b ; public: void nastav( int i, int j) { a=i;b=j; } void vypis(){ cout << a << ' '<< b <<"\n"; } } ; int main() { //objekty mají stejný typ i stejné jméno typu : moje_trida objekt1, objekt2; objekt1.nastav(10,4); vytvoření bitová kopie všech datových členů : objekt2 = objekt1; objekt1.vypis(); // výpis...10 4 objekt2.vypis(); // 10 4 system(“pause”) ; return 0; }
objekt2 = objekt1; // chyba, nestejný typ!! Příklad chybného přiřazení: class moje_trida { int a; public: ......stejné }; //třída stejná fyzicky, ale rozdílné jméno: class tvoje_trida{ } ; int main() { //objekty jsou fyzicky stejné, ale nemají stejná typová jména: moje_trida objekt1; tvoje_trida objekt2; objekt2 = objekt1; // chyba, nestejný typ!!
Kopírovací konstruktor. – úvod. Není-li uveden ve třídě, která je typem přiřazovaných objektů deklarován žádný kopírovací konstruktor, překladač sám vytvoří implicitní kopírovací konstruktor. Takto vytvořený konstruktor bude - veřejně přístupný - kopírovat jednotlivé složky pomocí jejich kopírovacích konstruktorů. Při přiřazování objektů, u kterých byla provedena dymamická alokace paměti je nutné sledovat - efekt spojený s překopírováním obsahu ukazatele na alokovanou paměť - následné chování destruktoru !! Implicitní kopírovací konstruktor vytvoří mělkou kopii, což zpravidla nechceme. Pří kopírování umožní přepsát obsah ukazatele na dynamickou proměnnou objektu. Tomu zabrání definice vlastního kopírovacího konstruktoru, který vytvoří hlubokou kopii. Příklad efektu spojeného s překopírováním obsahu ukazatele na alokovanou paměť při přiřazení objektů (mělká kopie ) včetně následné chování destruktoru:
#define SIZE 255 class strtype{ char *p_retezec; int delka; public: strtype(char *p_zdroj); ~strtype(); void vypis(); };//…………………………………. strtype::strtype( char *p_zdroj) { delka = strlen(p_zdroj); p_retezec=(char*)malloc(delka+1); if( !p_retezec){ cout<<"Alokace chybna !\n"; exit(1); }// end if strcpy(p_retezec,p_zdroj);// zápis }//………………………….. strtype::~strtype() free(p_retezec); }//…………………………….. void strtype::vypis() { cout << p_retezec <<" - delka : "<<delka; cout<<"\n"; }//……………………………………………….. int main() strtype objekt1(„AHOJ"), objekt2(„NAZDAR"); cout << "Vypis pred prirazenim :\n"; objekt1.vypis(); objekt2.vypis(); objekt2 = objekt1; ukazatel objektu 2 byl přepsán, ukazuje na alokovanou paměť objektu1 : cout << "\n\nVypis po prirazeni:\n"; system("Pause"); return 0; /* Zavoláním destruktorů uvolníme paměť alokovanou v objektu 1 dvakrát, alokovaná paměť v objektu 2 zůstane neuvolněna.*/ }
3.2.1. Způsoby předávání parametrů funkcím: 1)Předání hodnotou, funkce pracuje s kopiemi předávaných parametrů. Změny provedené ve funkci se neprojeví originálu. Použitelné v C i v C++. 2) Předání odkazem - referencí. Pomocí reference lze vytvořit jiné jméno pro tutéž proměnnou – zlepší čitelnost programů. Deklarace reference je podobná deklaraci ukazatele, avšak * nahradíme &. Znak & nemá v tomto kontextu význam adresního operátoru. Referenci musíme ihned v deklaraci inicializovat!!! Syntaxe reference: typ &nové_jméno = jméno; Př.: int pocitadlo; int &pocet_kusu = pocitadlo; Reference se nejčastěji používají pro předávání parametrů odkazem a za povinnou inicializaci reference je potom považováno předání skutečného parametru funkci. Změny provedené ve funkci se uplatní v originálu. Zavedeno až v jazyku C++. 3) Použitím ukazatelů
Definice funkcí: void prirustek1( int a, int b) { a++; b++; cout<<"Vypis ve funkci prirustek1( int a, int b) a= "; cout << a <<" b= "<< b <<endl; } void prirustek2( int& a, int& b) cout<<"Vypis ve funkci prirustek2( int& a, int& b) a="; cout<< a <<" b= "<< b <<endl; void prirustek3( int *a, int *b) (*a)++; (*b)++; cout<<"Vypis ve funkci prirustek3( int *a, int *b) a="; cout << *a <<" b= "<< *b <<endl; Deklarace funkcí: práce s kopií parametrů: void prirustek1( int a, int b); práce s originálem : void prirustek2( int& a, int& b); práce s originálem: void prirustek3( int *a, int *b); int main() { int x=1, z=2; Volání funkcí: prirustek1(x,z) ; předání hodnotou prirustek2(x,z); předání odkazem prirustek3(&x,&z); předání pomocí ukazatelů }
3.2.2 Předávání objektu funkci Objekty lze předávat funkcím stejně jako každá jiná data Parametry jsou typu třída, Objekt dané třídy je argumentem při volání funkce. Příklad předávání objektu funkci hodnotou: class trida{ int i; public: trida(int n) { i=n;} int predej_i( ) { return i; } }; // konec tridy int sqr_i ( trida objekt ) // počítá kvadrát atributu třídy return objekt . predej_i() * objekt . predej_i(); } int main() { trida objekt1(10); cout<<"Druha mocnina privatni promenne "; cout<< "i= "<< objekt1.predej_i() <<" objektu1 je "; cout << sqr_i (objekt1) << "\n"; return 0;
Příklad předávání objektu funkci ukazatelem. Argument použitý při volání fce je modifikován změnami, provedenými uvnitř této funkce. Př.: Prototyp funkce: sqr_i( trida *objekt ); Volání funkce: sqr_i( & objekt1) ; Argumentem je adresa předávaného objektu Příklad: class trida{ int i; public: trida(int n) { i=n; } void nastav_i(int n) { i = n; } int predej_i () { return i; } }; int sqr_i ( trida *objekt ) // parametrem je ukazatel na objekt { objekt->nastav_i( objekt->predej_i()*objekt-redej_i()); cout<< "ZMENA UVNITR FUNKCE sqr_i():"; cout<<objekt->predej_i(); }// konec funkce sqr_i(); pokračování
cout<<"Hodnota privatni promenne objektu i= "; pokračování int main() { trida objekt1(10); cout<<"Hodnota privatni promenne objektu i= "; cout << objekt1.predej_i()<<"\n"; // 10 sqr_i( &objekt1);// objekt1 je předán adresou cout <<"\n ve fci main() zustala zmena hodnoty "; cout <<"privatni promenne i a je rovna "; cout <<objekt1.predej_i(); getchar(); return 0; }//Výpis ve funkci 100, v main() 100.
Mechanismus volání konstruktoru a destruktoru pro objekt a kopii. Když je předáván objekt do funkce hodnotou, je vytvářena kopie tohoto objektu. Není volán konstruktor. Důvod: Konstruktor je obecně použit pro inicializaci určitých vlastností objektu při jeho počáteční konstrukci. Když je ale objekt předáván funkci, musí být předáván v aktuálním stavu. Destruktor. Když životnost funkce končí kopie je rušena, a je volán destruktor Destruktor může poškodit předávaný objekt, je-li v objektu provedena dynamická alokace paměti, kterou má za úkol uvolnit. Uvolnění paměti nastane již v okamžiku rušení kopie. . Tomu se lze vyhnout předáním objektu fci pomocí ukazatele nebo odkazem. Příklad: Fce int negace(dynamicka objekt) vrací zápornou hodnotu *objekt.p_i pokračování
#class dynamicka{ int *p_i; public: dynamicka( int i);// konstruktor ~dynamicka( ) { free(p_i); } int predej() { return *p_i; } }; //definice konstruktoru: dynamicka::dynamicka( int i ) { p_i = (int*)malloc(sizeof(int)); if( !p_i ) { cout << "Chyba alokace pameti"; exit(1); } *p_i = i; //................................................. int negace(dynamicka objekt) return - objekt.predej(); int main() { dynamicka objekt1(-10); cout << objekt1.predej() <<"\n"; cout << negace( objekt1) << "\n"; /*Při odchodu z fce negace() destruktor ruší kopie a uvolni tak alokovanou pamet pro promennou i na kterou odkazuje p_i, objekt je poškozen */ cout << objekt1.predej( ) <<"\n"; chyba ! system(„pause"); return 0; }// end main()
3.3. Vracení objektu funkcí. Funkce vrací objekt . Stejně jako lze objekty funkcím předávat, mohou funkce objekty navracet. Způsob vracení objektu funkcí: Když je vracen objekt, vytváří se dočasný objekt, ve kterém je uložena vracená hodnota. Jakmile je hodnota vrácena, je dočasný objekt zrušen. Příklad: class Vzorek { char retezec[80]; public: void vypis(){ cout<< retezec << "\n";} void nastav(char *zdroj ){strcpy( retezec,zdroj); } // zápis do atrubutu };// end třídy pokračování
//Definice funkce, která vrací objekt typu Vzorek : Vzorek vstup( ) //syntaxe - návratová hodnota typu třída { char zdroj[80]; Vzorek lokalni_objekt; // lokální proměnná cout <<"Napis retezec :"; cin >> zdroj; // zápis řetězce zdroj do retezec[80] - atribut lokalni_objekt pomocí nastav():: lokalni_objekt.nastav( zdroj); /*fce vstup() vrací lokalni_objekt typu Vzorek, do kterého byl zapsán retezec pomocí dočasného objektu : */ return lokalni_objekt; } end fce() int main( ) Vzorek objekt1; /*vracený dočasný objekt z fce vstup() je přiřazen do objekt1 a potom je zrušen:*/ objekt1 = vstup(); objekt1.vypis(); system("Pause"); return 0; }
Spřátelené funkce třídy - úvod. požadavek, aby funkce měla přístup k privátním členům dvou nebo více různých tříd, aniž by byla členem těcto tříd. Syntaxe: Spřátelená funkce je definována jako volná funkce, ale uvnitř deklarace třídy, které bude přítelem, musí být uvedeno před jejím prototypem klíčové slovo friend Příklad: class moje_trida{ int n,d; public: moje_trida( int i,int j) { n=i; d=j; } //Deklarace spřátelené funkce: friend int je_delitelem( moje_trida objekt ); };// end of class Spřátelená funkce není spojována s žádným objektem, není členskou funkcí. Doplníme-li deklaraci spřátelené funkce její definicí, bude s ní překladač zacházet jako s vloženou funkcí a to i v případě, že neuvedeme modifikátor inline. Spřátelená funkce může přístupovat k privátním prvkům pouze ve spojení s objektem.
//Definice spřátelené funkce: int je_delitelem ( moje_trida objekt ) { /*Spřátelená funkce má přístup k privátním atributům objektu. Není členem třídy, proto specifikuje privátní členy, ke kterým přistupuje pomocí jména objektu: */ if (!( objekt . n % objekt . d )) return 1; // je dělitelem else return 0; }// end fce int main( ) moje_trida objekt1(10,2), objekt2(13,3); if( je_delitelem(objekt1)) cout << "2 jsou delitelem 10\n"; cout <<"2 neni delitelem 10\n"; if( je_delitelem(objekt2)) cout << "3 jsou delitelem 13\n"; cout <<"3 neni delitelem 13\n"; system("PAUSE"); }
Jako přítele můžeme deklarovat - volnou funkci - metodu jiné třídy V tomto případě je nutné její identifikátor kvalifikovat jménem třídy, ke které patří Spřátelenou funkci nelze dědit. Obsahuje-li základní třída spřátelenou funkci, není tato funkce spřátelena s třídou odvozenou. Přátelé nejsou součástí třídy a proto se jich specifikace přístupových práv netýká. Příklad: Použití spřátelené funkce pro dva různé typy tříd je běžné využití spřátelené fce: Dva různé typy tříd mají společné veličiny, které se mají porovnat. třída osobní auto ... class osobni třída nákladní auto class nakladni Každá třída obsahuje privátní proměnnou rychlost kterou budeme porovnat pomocí spřátelené funkce porovnej_rychlost( osobni car, nakladni truck);
Spřátelená funkce porovnej_rychlost(osobni car, nakladni truck); je - vložena do obou tříd -přebírá parametry od třídy osobni i třídy nakladni. Proto musí nutně předbíhat deklaraci jedné z tříd. O této situaci musí být překladač informovat pomocí předběžné deklarace, jinak by signalizoval chybu. Způsob jak sdělit překladači jméno třídy bez její deklarace (předběžná deklarace třídy) vyžaduje vložení příkazu class jméno_třídy; před prvním použitím jména třídy. #include<iostream> // DOPŘEDNÁ DEKLARACE třídy nakladni: class nakladni; class osobni { int pocet_osob; int rychlost; //porovnávaný parametr public: // konstruktor : osobni( int posb, int speed ) { pocet_osob = posb; rychlost = speed; } pokračování
//spřátelená fce: friend int porovnej_rychlost(osobni car, nakladni truck); };// end třídy osobni class nakladni{ int vaha_nakladu; int rychlost; //porovnávaný parametr public: // konstruktor : nakladni( int naklad, int speed ) { vaha_nakladu = naklad; rychlost = speed; } friend int porovnej_rychlost(osobni car,nakladni truck); };// end třídy nakladni //Definice spřátelené funkce: int porovnej_rychlost(osobni car, nakladni truck) return car . rychlost - truck . rychlost;
Pokračování: int main( ) { int rozdil; osobni ford( 6,55), bmv( 2, 120); nakladni tatra111( 1000, 55), zil(1500,80); cout << "Porovnani ford a tatra111:\n"; rozdil = porovnej_rychlost( ford, tatra111); if( rozdil < 0) cout << "Nakladni je rychlejsi.\n"; else if( rozdil == 0) cout << "Stejna rychlost \n"; else cout << "osobni je rychlejsi \n"; system("Pause"); return 0; }
Spřátelená funkce může být členem jedné třídy a současně spřátelenou funkcí s jinou třídou: class nakladni { int vaha_nakladu; int rychlost; public: // konstruktor : nakladni( int naklad, int speed ) { vaha_nakladu = naklad; rychlost = speed; } //zde je spřátelenou fcí : friend int osobni::porovnej_rychlost(nakladni truck); };// end nakladni class osobni { int pocet_osob; int rychlost; public: // konstruktor : osobni( int posb, int speed ) { pocet_osob = posb; rychlost = speed; } // zde je členskou fcí: int porovnej_rychlost(nakladni truck); };// end třídy osobni int osobni::porovnej_rychlost(nakladni truck) { return rychlost - truck . rychlost; }
Třída jako přítel. Třídu deklarujeme jako přítele uvedením klíčového slova friend před class a jméno třídy. friend class jméno_třídy ; Tímto způsobem udělujeme právo přístupu k privátním členům třídy všem metodám přátelské třídy. Není významné, zda je deklarace friend uvedena v oblasti public, protected nebo private. Přiklad: class Atřída { private: int n; friend class Ftřída; // Ftřída je přítelem Atřídy public: void vypis(); }; // end Atřída class Ftřída { void nastav (Atřída obj;) //metoda přátelské třídy, má //přístup k privatni n { obj.n = 1; // Správně - Ftřída je přítelem Atřídy } } ; // end Ftřída
Jako přátelé třídy mohou být - funkce - celé třídy - jednotlivé metody tříd Příklad: class Atřída { friend int funkce( int i ); //přátelská funkce friend class Btřída; //přátelská třída friend int Ctřída:: metoda() /*přatelská metoda jiné třídy,třídy C */ } ; Třída Btřída je přátelská a proto za přátelské funkce jsou považovány všechny její metody bez toho, abychom je museli zvlášt´ rozepisovat.