LEKCJA 30: WYMIANA DANYCH MI�DZY OBIEKTAMI. ________________________________________________________________ W trakcie tej lekcji dowiesz si�, jak mo�na wymienia� dane i informacje pomi�dzy r�nymi obiektami. ________________________________________________________________ Hermetyzacja danych jest cenn� zdobycz�, ale od czasu do czasu obiekty powinny dokonywa� pomi�dzy sob� wymiany informacji, tak�e tych wewn�trznych - prywatnych. Ten problem mo�e sprawia� programi�cie troch� k�opot�w - nale�y zatem po�wi�ci� mu troch� uwagi. DOST�P DO DANYCH PRZY POMOCY FUNKCJI KATEGORII friend. Aby wyja�ni� mechanizmy dost�pu do danych obiekt�w b�dziemy potrzebowa�: * wielu obiekt�w; * danych prywatnych obiekt�w (dost�p do publicznych, "niezakapsu�kowanych" danych jest prosty i oczywisty); * funkcji o specjalnych uprawnieniach. Takie funkcje o specjalnych uprawnieniach - z mo�liwo�ci� odwo�ywania si� do prywatnych danych wielu obiekt�w (a nie tylko swojego) musz� w C++ posiada� status "friend" (ang. friend - przyjaciel). Nasz przyk�adowy program b�dzie operowa� tablic� z�o�on� z obiekt�w klasy Licznik. class Licznik { char moja_litera; int ile; public: void Inicjuj_licznik(char); void Skok_licznika(void); void Pokazuj(); }; ... Licznik TAB[MAX]; Obiekty - liczniki b�d� zlicza� wyst�pienie (ka�dy swojego) okre�lonego znaku w strumieniu znak�w wej�ciowych (wczytywanym z klawiatury). Tablica b�dzie si� sk�ada� z MAX == 26 element�w - obiekt�w - licznik�w, po jednym dla ka�dej du�ej litery alfabetu. Tablica b�dzie nazywa� si� TAB[26]. Po zadeklarowaniu: nazwa_klasy TAB[MAX]; kolejne obiekty b�d� si� nazywa�: nazwa_klasy Obiekt1 == TAB[0]; //Licznik 1 - 'A' nazwa_klasy Obiekt2 == TAB[1]; //Licznik 2 - 'B' ... ... nazwa_klasy ObiektN == TAB[N-1]; Po wprowadzeniu znaku z klawiatury wywo�amy wbudowan� do ka�dego obiektu funkcj� Skok_licznika(), kt�ra doda jedynk� do wewn�trznego licznika obiektu. Wywo�uj�c funkcj� zastosujemy zamiast typowej sk�adni ObiektK.Skok_licznika(); odpowiadaj�c� jej w tym wypadku notacj� TAB[i].Skok_licznika(); Powinni�my jeszcze przed wywo�aniem funkcji sprawdzi�, czy znak jest du�� liter� alfabetu. W przyk�adowym programie zrobimy to tak: ... cin >> znak; //Pobranie znaku z klawiatury for(int i = 0; i < 26; i++) { if(i == (znak - 'A')) TAB[i].Skok_licznika(); } ... Dzi�ki temu wewn�trzny licznik obiektu TAB[2] zostanie powi�kszony tylko wtedy, gdy znak - 'A' == 2 (znak jest liter� C, bo 'C' - 'A' == 2). Mo�na to zapisa� skuteczniej. ... cin >> znak; TAB[znak - 'A'].Skok_licznika(); //Inkrementacja licznika ... b�d� jeszcze kr�cej: ... TAB[getch() - 'A'].Skok_licznika(); ... Istnieje tu wszak�e niebezpiecze�stwo pr�by odwo�ania si� do nieistniej�cego elementu tablicy, przed czym powinni�my si� wystrzega�. W wyniku dzia�ania programu otrzymamy zliczon� ilo�� wyst�powania danej litery w strumieniu znak�w wej�ciowych. [P107.CPP] # include <ctype.h> //prototyp toupper() # include <iostream.h> class Licznik { char moja_litera; int ile; public: void Inicjuj(char); void Skok_licznika(); void Pokazuj(); }; void Licznik::Inicjuj(char z) { moja_litera = z; ile = 0; } void Licznik::Skok_licznika(void) { ile++; } void Licznik::Pokazuj(void) { cout << "Znak " << moja_litera << " wystapil " << ile << " razy" << '\n'; } main() { const MAX = 26; Licznik TAB[MAX]; register int i; /* inicjujemy liczniki: -------------------------------*/ for(i = 0; i < MAX; i++) { TAB[i].Inicjuj('A' + i); } /* pracujemy - zliczamy: -------------------------------*/ cout << "Wpisz ciag zankow zakonczony kropka [.]" << '\n'; for(;;) { char znak; cin >> znak; if(znak == '.') break; for(i = 0; i < MAX; i++) { if(i == (znak - 'A')) TAB[i].Skok_licznika(); } } /* sprawdzamy: ----------------------------------------*/ char sprawdzamy; cout << '\n' << "Podaj znak do sprawdzenia: " << '\n'; cin >> sprawdzamy; cout << "Wyswietlam wyniki zliczania: \n"; TAB[toupper(sprawdzamy) - 'A'].Pokazuj(); return 0; } Je�li chcieliby�my zliczy� ilo�� wszystkich wprowadzonych znak�w, powinni�my zsumowa� dane pobrane od wielu obiekt�w. Je�li dane przechowywane w obiektach maj� status danych prywatnych, to dost�p do tych danych mo�e by� utrudniony. Do tego momentu dost�p do danych prywatnych obiektu mogli�my uzyska� tylko pos�uguj�c si� autoryzowan� do tego metod� - w�asn� funkcj� wewn�trzn� tego� obiektu. Ale wtedy nie mieli�my dost�pu do danych innych obiekt�w a tylko do jednego - "w�asnego" obiektu funkcji. Je�li zatem chcieliby�my zsumowa� zawarto�ci wielu obiekt�w - licznik�w, to nale�y do tego zastosowa� tzw. funkcj� "zaprzyja�nion�" - friend function. Je�li deklaruj�c funkcj� zastosujemy s�owo kluczowe friend, to taka zaprzyja�niona z klas� funkcja uzyska prawo dost�pu do prywatnych element�w danej klasy. Zadeklarujemy tak� przyk�adow� zaprzyja�nion� funkcj� o nazwie Suma(). Funkcja b�dzie pobiera� jako parametr ilo�� obiekt�w do zsumowania i sumowa� zawarto�ci wewn�trznych licznik�w obiekt�w. const MAX = 26; class Licznik { char moja_litera; int ile; public: void Inicjuj(char); void Skok_licznika(); void Pokazuj(); friend int Suma(int); } TAB[MAX]; Zadeklarowana w taki spos�b zaprzyja�niona funkcja ma prawo dost�pu do prywatnych element�w wszystkich obiekt�w klasy Licznik. Typowe zastosowanie funkcji typu friend polega w�a�nie na dost�pie do danych wielu r�nych obiekt�w. Powinni�my zsumowa� zawarto�� p�l TAB[i].ile dla wszystkich obiekt�w (od i = 0 a� do i = MAX). Zwr�� uwag�, �e definiuj�c funkcj� Suma() nie stosujemy powt�rnie s�owa kluczowego friend. A oto definicja: int Suma(int ilosc_obiektow) { int i, suma = 0; for(i = 0; i < ilosc_obiektow; i++) suma += TAB[i].ile; return (suma); } Dzi�ki zastosowaniu s�owa "friend", funkcja Suma() jest zaprzyja�niona ze wszystkimi 26 obiektami, poniewa� wszystkie obiekty nale�� do tej klasy, w kt�rej zadeklarowali�my funkcj�: class ... { ... friend int Suma(...); ... } ... ; Tablica TAB[MAX] z�o�ona z obiekt�w klasy Licznik zosta�a zadeklarowana nazewn�trz funkcji main() ma wi�c status tablicy GLOBALNEJ. Funkcja Suma() ma dost�p do prywatnych danych wszystkich obiekt�w, mo�emy wi�c zastosowa� j� w programie w nast�puj�cy spos�b: [P108.CPP] # include <ctype.h> # include <iostream.h> class Licznik { char moja_litera; int ile; public: void Inicjuj(char); void Skok_licznika(); void Pokazuj(); friend int Suma(int); } const MAX = 26; Licznik TAB[MAX]; register int i; main() { /* inicjujemy liczniki: -------------------------------*/ for(i = 0; i < MAX; i++) { TAB[i].Inicjuj('A' + i); } /* pracujemy - zliczamy: -------------------------------*/ cout << "Wpisz ciag zankow zakonczony kropka [.]" << '\n'; for(;;) { char znak; cin >> znak; if(znak == '.') break; for(i = 0; i < MAX; i++) { if(i == (znak - 'A')) TAB[i].Skok_licznika(); } } /* sprawdzamy: ----------------------------------------*/ char sprawdzamy; cout << '\n' << "Podaj znak do sprawdzenia: " << '\n'; cin >> sprawdzamy; cout << "Wyswietlam wyniki zliczania: \n"; TAB[toupper(sprawdzamy) - 'A'].Pokazuj(); cout << "\n Wszystkich liter bylo " << Suma(MAX); return 0; } void Licznik::Inicjuj(char zn) { moja_litera = zn; ile = 0; } void Licznik::Skok_licznika(void) { ile++; } void Licznik::Pokazuj(void) { cout << "Znak " << moja_litera << " wystapil " << ile << " razy" << '\n'; } int Suma(int ilosc_obiektow) { int i, suma = 0; for(i = 0; i < ilosc_obiektow; i++) suma += TAB[i].ile; return (suma); } Tak dzia�a funkcja typu friend. Zwr��my tu uwag�, �e funkcja taka nie jest traktowana dok�adnie tak samo, jak metoda wchodz�ca w sk�ad klasy i obiektu. Metoda, czyli "w�asna" funkcja obiektu odwo�uje si� do jego pola (danych) w taki spos�b: void Licznik::Skok_licznika(void) { ile++; //Wiadomo o ktory obiekt chodzi } Funkcja klasy friend odwo�uje si� do p�l obiekt�w tak: int Suma(int liczba) { ... suma += TAB[i].ile; /* - wymaga dodatkowo wskazania, o kt�ry obiekt chodzi - */ } Nale�y pami�ta�, �e dla funkcji kategorii friend wszystkie obiekty nale��ce do danej klasy maj� status public - s� dost�pne. O ZAPRZYJA�NIONYCH KLASACH. W C++ mog� by� zaprzyja�nione ze sob� wzajemnie tak�e klasy. Pozwala to metodom zdefiniowanym wewn�trz jednej z klas na dost�p do prywatnych danych obiekt�w innych klas. W przypadku zaprzyja�nionych klas s�owem kluczowym friend poprzedzamy nazw� klasy (a nie ka�dej zap...
lazarusp22