lekcja31.txt

(12 KB) Pobierz
LEKCJA 31: PRZEKAZANIE OBIEKT�W JAKO ARGUMENT�W DO FUNKCJI.  
________________________________________________________________ 
W trakcie tej lekcji poznasz sposoby manipulowania obiektami  
przy pomocy funkcji. Poznasz tak�e troch� dok�adniej referencje. 
________________________________________________________________ 
 
Typowy spos�b przekazywania argument�w do funkcji w C++ to  
przekazanie przez warto�� (ang. by value). W przypadku obiekt�w  
oznacza to w praktyce przekazanie do funkcji kopii obiektu. Jako 
 
przyk�ad zastosujemy program zliczaj�cy wyst�pienia znak�w w  
strumieniu wej�ciowym. Zmienimy w tym programie spos�b  
wyprowadzenia wynik�w. Funkcji Pokazuj() przeka�emy jako  
argument obiekt. Obiekt-licznik zawiera w �rodku t� informacj�,  
kt�rej potrzebuje funkcja - ilo�� zliczonych znak�w. Zacznijmy  
od zdefiniowania klasy. 
  
class Licznik  
{  
public:  
  char moja_litera;  
  int ile;    
  Licznik(char litera);  
  void Skok_licznika();  
};  
 
W programie g��wnym mo�emy zastosowa� konstruktor do  
zainicjowania obiektu np. tak:  
 
main()  
{  
  Licznik licznik_a('a');  
 ... 
 
Zdefiniujmy funkcj�. Obiekt licznik_a b�dzie argumentem funkcji  
Pokazuj(). Funkcja powinna wyprowadzi� na ekran zawarto�� pola  
licznik_a.ile. Deklaracja - prototyp takiej pobieraj�cej obiekt  
funkcji b�dzie wygl�da� tak: 
 
wart_zwracana Nazwa_funkcji(nazwa_klasy nazwa_obiektu); 
 
Nazwa klasy spe�nia dok�adnie tak� sam� rol� jak ka�dy inny typ  
danych. W naszym przypadku b�dzie to wygl�da� tak: 
 
void Pokazuj(Licznik obiekt); 
 
Poniewa� "obiekt" jest parametrem formalnym i jego nazwa nie  
jest tu istotna, mo�emy pomin�� j� w prototypie funkcji (w  
definicji ju� nie!) i skr�ci� zapis do postaci:  
 
void Pokazuj(Licznik); 
 
Funkcja Pokazuj() otrzyma w momencie wywo�ania jako sw�j  
argument kopi� obiektu, kt�r� jako argument formalny funkcji  
nazwali�my "obiekt". W naszym programie wywo�anie tej funkcji  
b�dzie wygl�da� tak:  
 
  Pokazuj(licznik_a);  
 
Obiekt "licznik_a" jest tu BIE��CYM ARGUMENTEM FAKTYCZNYM. Typ  
(tzn. tu: klasa) argumentu faktycznego musi by� oczywi�cie  
zgodny z zadeklarowanym wcze�niej typem argumentu formalnego  
funkcji.  
 
Je�li funkcja dosta�a w�asn� kopi� obiektu, mo�e odwo�a� si� do  
element�w tego obiektu w taki spos�b:  
 
void Pokazuj(Licznik obiekt) 
{  
  cout << obiekt.ile;  
}  
 
albo np. tak:  
 
int Pokazuj(Licznik obiekt)  
{  
  return (obiekt.ile);  
} 
 
Nale�y podkre�li�, �e funkcja Pokazuj() NIE MA DOST�PU do  
oryginalnego obiektu i jego danych. Podobnie jak by�o to w  
przypadku przekazania zmiennej do funkcji i tu funkcja ma do  
dyspozycji WY��CZNIE SWOJ� "PRYWATN�" KOPI� obiektu. Funkcja nie 
 
mo�e zmieni� zawarto�ci p�l oryginalnego obiektu.  
 
Podobnie, jak w przypadku "zwyk�ych" zmiennych, je�li chcemy by  
funkcja dzia�a�a na polach oryginalnego obiektu, musimy funkcji  
przekaza� nie kopi� obiektu a wska�nik (pointer) do tego  
obiektu. Oto program przyk�adowy w ca�o�ci:  
 
[P110.CPP]  
 
//UWAGA: Program moze wymagac modelu wiekszego niz SMALL !  
  
# include "ctype.h"   
# include "iostream.h"    
   
class Licznik    
{     
public:   
  char moja_litera;     
  int ile;    
  Licznik(char);    
  void Skok_licznika();     
};   
   
/* Prototypy funkcji (dwie wersje): ---------------- */  
  
void Pokazuj1(Licznik);  
  
int Pokazuj2(Licznik);  
  
  
void main()     
{     
/* inicjujemy licznik:  -------------------------------*/     
    
Licznik licznik_a('a');  
   
/* pracujemy - zliczamy: -------------------------------*/    
    
cout << "Wpisz ciag zankow zakonczony kropka [.]" << '\n';    
for(;;)     
  {   
    char znak;    
    cin >> znak;    
    if(znak == '.') break;    
if (znak == licznik_a.moja_litera) licznik_a.Skok_licznika();   
  }    
  
/* sprawdzamy: ----------------------------------------*/    
    
cout << "Wyswietlam wyniki zliczania litery a: \n";     
  
  Pokazuj1(licznik_a);  
  cout << '\n' << Pokazuj2(licznik_a);  
  
}    
   
Licznik::Licznik(char z)     
{     
  moja_litera = z;     
  ile = 0;     
}     
    
void Licznik::Skok_licznika(void)     
{     
  ile++;     
}     
  
/* ------------ Definicje funkcji: ---------------- */  
  
void Pokazuj1(Licznik Obiekt)  
{  
  cout << Obiekt.ile;  
}  
  
int Pokazuj2(Licznik Obiekt)  
{  
  return (Obiekt.ile);  
}       
 
[!!!]UWAGA:  
________________________________________________________________ 
Programy manipuluj�ce obiektami w taki spos�b mog� wymaga�  
modelu pami�ci wi�kszego ni� przyjmowany domy�lnie model SMALL.  
Typowy komunikat pojawiaj�cy si� przy zbyt ma�ym modelu pami�ci  
to:  
 
Error 43: Type mismatch in parameter to call to  
Pokazuj1(Licznik)...  
(��y typ argumentu przy wywo�aniu funkcji Pokazuj(...)...)  
 
Programy obiektowe s� z regu�y szybke, ale niestety do��  
"pami�cioch�onne". W IDE BORLAND C++ masz do dyspozycji opcj�:  
 
Options | Compiler | Code generation | Model  
 
Dok�adniejsze informacje o modelach pami�ci znajdziesz w dalszej 
 
cz�ci ksi��ki.  
________________________________________________________________ 
 
 
O PROBLEMIE REFERENCJI.  
 
Typowy (domy�lny) spos�b przekazywania argument�w do funkcji w  
C++ polega na tzw. "przekazaniu przez warto��" i jest inny ni�  
Pascalu, czy Basicu. Poniewa� w polskich warunkach do C/C++  
wi�kszo�� adept�w "dojrzewa" po przebrni�ciu przez Basic i/lub  
Pascal, programi�ci ci obci��eni s� ju� pewnymi nawykami i  
pewnym schematyzmem my�lenia, kt�ry do C++ niestety nie da si�  
zastosowa� i jest powodem wielu pomy�ek. To, co w Basicu wygl�da 
 
zrozumiale (uwaga, tu w�a�nie pojawia si� automatyzm my�lenia):  
 
PRINT X            REM Wyprowad� bie��c� warto�� zmiennej X  
INPUT X            REM Pobierz warto�� zmiennej X  
 
a w Pascalu:  
 
writeln(X);        { Wyprowad� bie�ac� warto�� zmiennej X }  
readln(X);         { Pobierz warto�� zmiennej X }  
 
przyjmuje w C/C++ form� zapisu wyra�nie dualnego:  
 
printf("%d", X);   //Wyprowad� warto�� zmiennej X  
scanf("%d", &X);   //Pobierz warto�� zmiennej X 
 
Na czym polega r�nica? Je�li odrzucimy na chwil� automatyzm i  
zastanowimy si� nad t� sytuacj�, zauwa�ymy, �e w pierwszym  
przypadku (wyprowadzanie istniej�cych ju� danych - PRINT,  
wrilteln, printf()) w celu poprawnego dzia�ania funkcji  
powinni�my przekaza� jej BIE��C� WARTO�� ARGUMENTU X (adres  
zmiennej w pami�ci nie jest funkcji potrzebny). Dla Basica,  
Pascala i C++ bie��ca warto�� zmiennej kojarzoana jest z jej  
identyfikatorem - tu: "X". W drugim jednak�e przypadku (pobranie 
 
danych i umieszczenie ich pod w�a�ciwym adresem pami�ci) jest  
inaczej. Funkcji zupe�nie nie interesuje bie��ca wart�� zmiennej 
 
X, jest jej natomiast do poprawnego dzia�ania potrzebny adres  
zarezerwowany dla zmiennej X w pami�ci. Ale tu okazuje si�, �e  
Basic i Pascal post�puj� dok�adnie tak samo, jak poprzednio:  
 
INPUT X      i      read(X);  
 
Oznacza to, �e X nie oznacza dla Pascala i Basica bie��cej  
warto�ci zmiennej, lecz oznacza (DOMY�LNIE) przekazanie do  
funkcji adresu zmiennej X w pami�ci. Funkcje oczywi�cie  
"wiedz�", co dosta�y i dalej ju� one same manipuluj� danymi we  
w�a�ciwy spos�b.  
 
W C++ jest inaczej. Zapis:  
 
Funkcja(X);  
 
oznacza w praktyce, �e zostan� wykonane nast�puj�ce operacje:  
 
* spod adresu pami�ci przeznaczonego dla zmiennej X zostanie  
(zgodnie z zadeklarowanym formatem) odczytana bie��ca warto��  
zmiennej X;  
* warto�� X zostanie zapisana na stos (PUSH X);  
* zostanie wywo�ana funkcja Funkcja();  
* Funkcja() pobierze sobie warto�� argumentu ze stosu (zgodnie z 
 
formatem zadeklarowanym w prototypie Funkcji()).  
* Funkcja() zadzia�a zgodnie ze swoj� definicj� i je�li ma co�  
do pozostawienia (np. return (wynik); ) pozostawi wynik.  
 
Jak wida�:  
 
* funkcja "nie wie", gdzie w pami�ci umieszczony by� przekazany  
jej argument;  
* funkcja komunikuje si� "ze �wiatem zewn�trznym" (czyli w�asnym 
 
programem, b�d� funkcj� wy�szego rz�du - wywo�uj�c�) tylko za  
po�rednictwem stosu;  
* funkcja dostaje swoj� "kopi�" argumentu z kt�rym dzia�a;  
* funkcja nie ma wp�ywu na "orygina�" argumentu, kt�ry pozostaje 
 
bez zmian.  
 
REFERENCJA - CO TO TAKIEGO ?  
 
Zastan�wmy si�, czym w�a�ciwie jest referencja zmiennej w C++.  
Pewne jest, �e jest to alternatywny spos�b odwo�ania si� do  
zmiennej. Zacznijmy od trywialnego przyk�adu odwo�ania si� do  
tej samej zmiennej maj�cej swoj� w�a�ciw� nazw� "zmienna" i  
referencj� "ksywa".  
 
# include "iostream.h"  
 
main()  
{  
  int zmienna;  
  int& ksywa;  
 ...  
 
Aby "ksywa" oznacza�a t� sam� zmienn�, referencj� nale�y  
zainicjowa�:  
 
int& ksywa = zmienna;  
 
Zainicjujemy nasz� zmienn� "zmienna" i b�dziemy robi� z ni�  
cokolwiek (np. inkrementowa�). R�wnocze�nie b�dziemy sprawdza�,  
czy odwo�ania do zmiennej przy pomocy nazwy i referencji b�d�  
pozostawa� r�wnowa�ne.  
 
[P111.CPP] 
 
/* UWAGA: Program moze potrzebowac modelu wiekszego niz   
  domyslnie ustawiany MODEL SMALL               */  
 
# include "iostream.h"   
  
main()   
{   
  int zmienna = 6666;   
  int& ksywa = zmienna;   
  
  cout << '\n' << "Zmienna" << " Ksywa";  
  cout << '\n' << zmienna << '\t' << ksywa;   
  
  for (register int i = 0; i < 5; i++, zmienna += 100)   
      cout << '\n' << zmienna << '\t' << ksywa;  
  
  return 0;   
}  
  
Dialog (a w�a�ciwie monolog) powinien wygl�da� tak:  
 
C:\>program  
  
Zmienna Ksywa  
6666    6666 
6666    6666  
6766    6766  
6866    6866  
6966    6966  
7066    7066 ...
Zgłoś jeśli naruszono regulamin