background image

Referencje

Referencja  jest  czymś  w  rodzaju  synonimu  lub  odsyłacza.  Gdy  tworzy  się 
referencje, to trzeba ją zainicjalizować nazwą innego, docelowego obiektu. Od 
tego  momentu  referencja  spełnia  rolę  alternatywnej  nazwy  dla  obiektu  i 
wszystkie  operacje  wykonywane  na  referencji  będą  również  wykonywane  na 
obiekcie docelowym.
Niektórzy programiści twierdzą, że referencja to wskaźnik. 

Nie jest to prawdą!

 

Oczywiście, zazwyczaj referencje tworzone są z wykorzystaniem wskaźników, 
ale jest to sprawa tylko i wyłącznie twórców kompilatora. 

Trzeba rozróżniać te dwa pojęcia:

 wskaźniki są zmiennymi przechowującymi adresy innych obiektów. 

 referencje są "synonimami" lub odsyłaczami do obiektów.

Tworzenie referencji

Referencję  tworzy  się  poprzez  wpisanie  nazwy  typu  docelowego 
poprzedzonego symbolem operatora referencji ( & ). Następnie należy podać 
nazwę  dla  referencji.  Nazwa  referencji  może  być  dowolną,  poprawną  nazwą 
C++. 

Przyjmijmy że będziemy referencje nazywać zaczynając nazwę od litery r. 

background image

Przykład:

Jeżeli mamy zmienną całkowitą nPom, to można stworzyć do niej referencję w 
następujący sposób:

int &rPom = nPom ;

Taką deklarację można przeczytać: "rPom jest referencją do zmiennej typu int 
zainicjowaną tak, aby odsyłała do zmiennej nPom
". 

Przykład:

Zauważ,  że  referencja  jest  od  razu 
inicjalizowana. 

Gdybyśmy 

pominęli 

inicjalizację, kompilator zgłosiłby błąd. 

background image

Wykorzystanie operatora adresu na 

referencjach

Jeżeli  pobierze  się  adres  referencji,  to  otrzyma  się  adres  zmiennej,  którą  ta 
referencja  reprezentuje.  Taka  jest  natura  referencji  -  są  one  odsyłaczami  do 
obiektów docelowych. 

Przykład:

W  C++  nie  ma  możliwości  otrzymania  fizycznego  adresu  referencji  gdyż 
byłoby  to  zbędne  (w  przeciwieństwie  do  wskaźników).  Referencje  są 
inicjalizowane  w  momencie  tworzenia  i  zawsze  stanowią  synonim  obiektu 
docelowego, nawet w przypadku użycia operatora adresu.

background image

Jeżeli mielibyśmy klasę o nazwie Prezydent, to wówczas można zadeklarować 
obiekt tej klasy następująco:

Przykład:

Prezydent oLechWalesa;

Prezydent &rWalesa = oLechWalesa;

Teraz można zadeklarować referencję do obiektu klasy Prezydent:

W pamięci będzie istnieć tylko jeden obiekt. Obydwa stworzone identyfikatory 
odnoszą się do tego obiektu. Każda operacja wykonana na obiekcie  rWalesa 
zostanie również wykonana na oLechWalesa.

Zapamiętaj:  aby  odróżniać  symbol  &  który  deklaruje  referencje  (o 
nazwie  rWalesa)  do  obiektu  klasy  lub  zmiennej,  a  symbolem  & 
zwracającym  adres  zmiennej.  Normalnie,  przy  wykorzystywaniu 
referencji,  nie  jest  potrzebny  operator  adresu.  Po  prostu  używa  się 
referencji tak, jak obiektu docelowego. 

Czasami  nawet  doświadczeni  programiści  nie  są  pewni,  co  się  stanie,  przy 
próbie  ponownego  przypisania  do  referencji.  Okazuje  się,  że  taka  operacja 
powoduje  przypisanie  nowej  wartości  do  obiektu  docelowego  (co  po  chwili 
namysłu jest oczywiste i zgodne z definicją referencji). 

background image

Przykład:

Rezultat  tej  operacji  jest  inny  niż 
zamierzony.  rRef  nadal  jest  powiązana 
ze  zmienną  nPom1,  lecz  zmianie  uległa 
wartość, która jest teraz równa 8.

 

Chcieliśmy zmienić znaczenie referencji 
rRef i przypisać jej nowy obiekt 
docelowy – nPom2. 

Dzieje się tak dlatego ponieważ przypisanie:

nPom1 = nPom2 ;

rRef = nPom2 ;

background image

Do czego można tworzyć referencje?

Referencje  można  stworzyć  dla  dowolnego  obiektu,  niezależnie  od  tego,  czy 
jest  on  standardowym  obiektem  C++  czy  też  zdefiniowanym  przez 
użytkownika. Ważne jest, że referencja odnosi się do konkretnego obiektu, a 
nie do klasy. 

Oznacza to, że nie można napisać tak: int  &rIntRef = int

Trzeba zainicjować referencje za pomocą konkretnej zmiennej typu int:

int nPom = 200;
int &rIntRef = nPom ;

Podobnie  rzecz  się  ma  z  obiektami  zdefiniowanymi  przez  użytkownika.  Nie 
można napisać tak:

KOT &rKotRef = KOT 

Tutaj, podobnie jak w przypadku zmiennej całkowitej, trzeba zainicjować 
referencję za pomocą konkretnego obiektu klasy KOT:

KOT oFilemon ;
KOT &rKotRef = oFilemon ;

Referencje do obiektów wykorzystuje się tak, jak obiekty. Dane i funkcje 
wewnętrzne dostępne są z wykorzystaniem operatora dostępu do klasy - 
kropki ( . ).

background image

Puste (null) wskaźniki i puste (null) referencje.

Kiedy wskaźnik jest niezainicjowany lub ewentualnie skasowany za pomocą 
delete, to powinna być my przypisana wartość null (0). 

Referencja  nie  może  być  pusta  (  null  ).  Program,  w  którym  znajdują  się 
referencje  do  obiektów  null  jest  błędny  i  jego  działanie  jest 
nieprzewidywalne.  Może  on  działać  pozornie  prawidłowo,  lecz  w  pewnym 
momencie może np. niespodziewanie skasować wszystkie zbiory na dysku.

Ta reguła nie obowiązuje w przypadku referencji !

Większość  kompilatorów  honoruje  puste  obiekty.  Problemy  pojawiają  się 
jedynie przy próbie wykonania operacji na takich obiektach. Pamiętajcie, że w 
takiej sytuacji, próba przeniesienia programu pod inny kompilator lub na inną 
maszynę  może  skończyć  się  niepowodzeniem  jeśli  to  inne  środowisko  nie 
zezwala na istnienie pustych ( null ) obiektów.

background image

Przekazywanie argumentów funkcji przez 
referencje.

Powiedzieliśmy sobie, że funkcje mają dwa ograniczenia: 

  

argumenty są przekazywane przez wartość 

  funkcja może zwrócić tylko jedną  wartość.

Przekazywanie  argumentów  przez  referencje  pozwala  na  obejście  obydwu 
tych ograniczeń. 
W C++, przekazywanie przez referencje, może być przeprowadzone 
na dwa sposoby: 

  

wykorzystaniem wskaźników, 

  referencji. 

Wewnątrz funkcji nie jest tworzona kopia przekazywanego obiektu. Do 
funkcji przekazywany jest oryginalny obiekt.
Przekazywanie obiektów przez referencje pozwala funkcji na 
wykonywanie modyfikacji w tych obiektach.

Każde rozwiązanie ma własną składnię jednak efekt końcowy jest taki 
sam. 

background image

Przykład:

Problem  polega  tutaj  na 
tym, że zarówno x jak i y są 
przekazywane 

przez 

wartość.  Oznacza  to,  że  są 
tworzone lokalne kopie tych 
zmiennych 

wszystkie 

operacje są wykonywane na 
kopiach,  nie    powodując 
żadnych  zmian  zmiennych 
oryginalnych. 

To, 

czego 

tutaj 

potrzeba, 

to 

przekazywanie  x  i  y  przez 
referencje.

Można ten problem rozwiązać na dwa sposoby: 
1. można zamienić parametry funkcji Zamien() na wskaźniki do oryginalnych 

wartości 

2. przekazywać referencje.

background image

Ad.1. Implementacja funkcji Zamien () z wykorzystaniem 
wskaźników

Kiedy  przekazuje  się  wskaźnik,  to  faktycznie  przekazuje  się  adres  zmiennej, 
co  pozwala  funkcji  na  manipulowanie  jej  wartością.  Żeby  funkcja  Zamien  () 
mogła  zamieniać  dwie  zmienne  wartościami,  to  trzeba  ją  zadeklarować  tak, 
aby  jako  parametry  pobierała  dwa  wskaźniki  do  zmiennych  typu  int.  Taki 
zabieg  pozwoli  funkcji  na  pośrednie  odwołanie  się  do  tych  zmiennych  i 
zamianę ich wartości. 

Funkcja  Zamien  ()  jest  wywoływana  z 
parametrami  którymi  są  adresy  zmiennych, 
które chcemy zamienić. 

Zmienna lokalna nTemp nie musi być wskaźnikiem ponieważ 
jej zadaniem jest przechowanie wartości *pX

background image

Ad.2. Implementacja funkcji Zamien () z wykorzystaniem 
referencji

Poprzedni  program  działa,  lecz  funkcja  Zamien  ()  jest  raczej  nieporęczna  z 

następujących powodów:

1. wielokrotne  pośrednie  odwoływanie  się  do  zmiennych  wskazywanych 

powoduje, że takie rozwiązanie jest podatne na błędy

2. konieczność  przekazywania  adresów  zmiennych  powoduje  "ujawnienie" 

wewnętrznych mechanizmów funkcji Zamien () .

Zamierzeniem C++ jest oddzielenie użytkownika funkcji od jej wewnętrznych 
mechanizmów  i  implementacji.  Przekazywanie  wskaźników  przenosi  część 
ciężaru wykonania funkcji na funkcję wywołującą (na użytkownika). 

Tym razem, bezpośrednio przekazywane są do 
funkcji zmienne, a nie ich adresy. 

W  funkcji  Zamien  ()  zmienne  x  i  y  są 
identyfikowane  przez  referencje.  Można  tak 
napisać,  gdyż  jak  już  wcześniej  wielokrotnie 
mówiłem,  stanowią  one  odnośniki,  synonimy 
oryginalnych zmiennych.

background image

Interpretacja nagłówków i prototypów funkcji

Poprzez  analizę  zadeklarowanych  parametrów  w  prototypie,  który  zazwyczaj 
znajduje  się  w  specjalnym  pliku  nagłówkowym  (*.hpp  ,  *h  )  wraz  z  innymi 
prototypami,  programista  dowiaduje  się,  że  parametry  przekazywane  do 
funkcji  zamień  są  przekazywane  przez  referencje,  co  w  przypadku  funkcji 
Zamien () oznacza, że zostaną one prawidłowo zamienione.

Skąd jednak funkcja wywołująca wie, czy wartości są przekazywane 
przez referencje czy przez wartość?

Zwracanie wielu wartości 

Jak już mówiliśmy, każda funkcja, normalnie może zwrócić tylko jedną 
wartość. 

Jedna z możliwości polega na przekazaniu do funkcji przez referencje dwóch 
roboczych  obiektów.  Funkcja  może  nadać  im  wymagane  wartości. 
Przekazanie  przez  referencje  gwarantuje,  ze  przypisanie  wartości  nastąpi  do 
obiektów  oryginalnych,  co  umożliwi  ich  odczytanie.  W  efekcie  uzyskujemy 
funkcję, która zwraca dwie wartości. 

Co jednak zrobić, gdy chcemy zwrócić z funkcji dwie wartości? 

Takie  rozwiązanie  pozwala  na  ominięcie  normalnej  wartości  zwracanej  przez 
funkcję,  którą  można  w  tym  momencie  wykorzystać  np.  do  zwracania  kodu 
błędu.

background image

Zmienna 

nLiczba, 

adresy 

dwóch 

pozostałych  zmiennych,  są  przekazywane 
do funkcji Potegi () .

Dodatkowe  wartości,  kwadratu  i  sześcianu  podanej 
liczby, 

nie 

są 

zwracane 

wykorzystaniem 

mechanizmów  oferowanych  przez  return.  Zmieniona 
zostaje  zawartość  pamięci  wskazywanej  przez 
przekazane  do  funkcji  wskaźniki,  co  powoduje 
modyfikację 

wartości 

zmiennych 

funkcji 

wywołującej, do których te adresy nalezą.

background image

Ten  program  można  udoskonalić,  np.  poprzez  wprowadzenie  deklaracji  dla 
wartości błędu i sukcesu:

enum WARTOSC_BLEDU {SUKCES, PORAZKA};

Teraz, zamiast zwracać niewiele mówiące wartości 0 lub 1, możemy zwracać 
SUKCES albo PORAZKA.

Zwracanie wartości przez referencje

background image

Efektywność przekazywania przez referencje

Za  każdym  razem,  kiedy  przekazuje  się  przez  wartość  obiekt  do  funkcji, 
tworzona  jest  kopia  obiektu.  Również,  kiedy  zwraca  się  obiekt  z  funkcji  (za 
pomocą  return)  jest  tworzona  kopia.  W  przypadku  dużych,  stworzonych 
przez  użytkownika  obiektów,  koszt  tworzenia  kopii  jest  znaczący. 
Wykorzystywane jest więcej pamięci niż faktycznie potrzeba i program działa 
wolniej.
Rozmiar  obiektu  stworzonego  przez  użytkownika  na  stosie  jest  równy  sumie 
wielkości jego zmiennych wewnętrznych. Każda zmienna wewnętrzna również 
może być kolejnym obiektem stworzonym przez użytkownika. Przekazywanie 
tak złożonego i dużego obiektu i kopiowanie go na stos może bardzo obciążać 
system  i  spowalniać  działanie  programu,  przy  jednoczesnym  wzroście 
zapotrzebowania na pamięć.
Występuje tutaj jeszcze jeden, dodatkowy koszt. 

Każda kopia obiektu jest wykonywana za pomocą specjalnego konstruktora, a 
mianowicie  konstruktora  kopiującego.  Kiedy  taki  chwilowy  obiekt  jest 
kasowany (np. w momencie wyjścia z funkcji) to zostaje wywołany destruktor 
klasy,  do  której  ten  obiekt  należy.  Jeśli  obiekt  jest  wartością  zwracaną,  to 
również musi być najpierw stworzony, a następnie skasowany.
W  przypadku  dużych  obiektów,  zarówno  konstruktor  jak  i  destruktor, 
pochłaniają dużo czasu procesora i pamięci. 

background image

Przykład:

Rzeczywisty  obiekt  byłby  dużo  większy, 
ale  ten  przykład  dostatecznie  dobrze 
pokazuje  jak  często  są  wywoływane 
konstruktory  i  destruktory  w  trakcie 
normalnej pracy programu.

background image

Ponieważ  Funkcja1()  pobiera  obiekt  klasy  ZwyklyKot  przez  wartość,  dlatego 
na stosie musi zostać stworzona kopia obiektu. Zostaje wywołany konstruktor 
kopiujący.  Wartość  zwracana  z  Funkcja1()  nie  jest  przypisywana  do  żadnego 
obiektu,  zatem  obiekt  tworzony  w  momencie  wyjścia  z  funkcji  jest 
natychmiast  wyrzucany  z  pamięci  poprzez  wywołanie  destruktora.  Ponieważ 
Funkcja1()  się  skończyła,  dlatego  lokalna  kopia  obiektu  również  ulega 
skasowaniu poprzez wywołanie destruktora.

W funkcji Funkcja2 () parametr jest przekazywany przez wskaźnik. Jak widać 
na  wydruku,  nie  jest  tworzona  żadna  kopia  obiektu.  Funkcja2  ()  wypisuje 
komunikat  i  zwraca  wskaźnik  do  obiektu  klasy  ZwyklyKot.  Znowu  nie  jest 
wywoływany ani konstruktor ani destruktor klasy.

Jak  widać,  wywołanie  funkcji  Funkcja1  (),  ze  względu  na  przekazywanie 
parametrów przez wartość, spowodowało dwukrotne wywołanie konstruktora 
i  destruktora,  w  przeciwieństwie  do  funkcji  Funkcja2  (),  której  wywołanie 
odbywało się bez uczestnictwa konstruktora i destruktora klasy.

background image

Przekazywanie stałych (const) wskaźników

Mimo  że  przekazywanie  wskaźników  jest  bardzo  efektywne,  to  jest  ono 
jednocześnie  bardzo  niebezpieczne.  Funkcja2  ()  (z  ostatniego  programu)  nie 
ma prawa zmieniać przekazanego obiektu, który aktualnie jest przekazywany 
w  formie  adresu.  Takie  rozwiązanie  niesie  ze  sobą  niebezpieczeństwo 
nieświadomej  (spowodowane  błędem  programisty)  zmiany  oryginalnego 
obiektu,  w  przeciwieństwie  do  przekazywania  przez  wartość,  gdzie  obiekt 
oryginalny jest "bezpieczny".
Przekazywanie przez wartość, to jakby dawanie do muzeum fotografii dzieła 
sztuki,  a  nie  oryginalnego  dzieła.  Jeśli  ktoś  zniszczy  kopie  to  oryginał  i  tak 
zostanie  nienaruszony.  Przekazywanie  przez  referencje  można  porównać  do 
wysyłania do muzeum swojego, domowego adresu i zaproszenia do oglądania 
własnych zasobów.

Rozwiązaniem  tego  problemu  jest  przekazywanie  stałych  (const
wskaźników. Takie posunięcie zabezpiecza przed użyciem metod, które nie są 
zadeklarowane jako const, przez co mogą zmieniać obiekt. 

background image

Przykład:

W  tym  przykładzie  konstruktor  kopiujący  nie  jest  nigdy  wywoływany, 
ponieważ  obiekt  jest  przekazywany  do  funkcji  przez  wskaźnik  i  nie  jest 
wykonywana jego kopia.

Parametr  i  wartość  zwracana  zostały  zadeklarowane  tak,  aby  funkcja 
pobierała  stały  wskaźnik  (const)  do    stałego  (czyli  również  const)  obiektu  i 
żeby zwracała również stały wskaźnik do  stałego obiektu. Ponieważ zarówno 
parametr jak i wartość zwracana są przekazywane przez wskaźnik, to nie jest 
tworzona  żadna  kopia  obiektu  i,  co  się  z  tym  ściśle  wiąże,  nie  jest 
wywoływany konstruktor kopiujący. Wskaźnik występujący w funkcji Funkcja2 
()  jest  stały  i  nie  mamy  możliwości  wykorzystania  go  do  wywołania  funkcji 
UstawWiek () (zadeklarowanej bez użycia słowa kluczowego const). 

Gdybyśmy usunęli komentarz to kompilator 
zgłosiłby błąd.

background image

Kiedy wykorzystywać referencje, a kiedy 
wskaźniki?

Programiści  C++  preferują  referencje.  Są  łatwiejsze,  bardziej  eleganckie  i 
pozwalają na lepsze ukrycie szczegółów implementacyjnych.

  Jednak  raz  stworzona  referencja  nie  może  być  przypisana  do  innego 
obiektu. 

  Jeżeli  chce  się  mieć  wskazanie  najpierw  na  jeden  obiekt,  a  następnie  na 
drugi, trzeba wykorzystać wskaźniki. 

  Referencje  nie  mogą  być  puste  (null)  i  jeżeli  jest  możliwość,  że  obiekt 
będzie  pusty  to  wykorzystanie  referencji  w  takim  przypadku  jest  błędem, 
trzeba użyć wskaźnika.

Inny problem pojawia się przy wykorzystaniu operatora  new. Jeżeli  new nie 
jest  w  stanie  zarezerwować  pamięci  na  stercie  to  zwraca  pusty  (null) 
wskaźnik.  Referencja  nie  może  być  pusta  dlatego  nie  wolno  inicjalizować 
referencji  do  zarezerwowanej  pamięci  zanim  nie  upewnimy  się,  że  wskaźnik 
nie jest pusty. 

Przykład:

Oto przykładowa sekwencja instrukcji, która prawidłowo obsługuje tworzenie 
referencji do pamięci na stercie:

int *pInt = new int; 
if (pInt != NULL)
{
  int &rInt = *pInt;
}

background image

Nie zwracaj referencji do obiektów lokalnych 

Często,  kiedy  programista  pozna  referencje,  to  wykorzystuje  je,  gdzie  się 
tylko  da.  Można  wtedy,  mówiąc  prostym  językiem,  "przedobrzyć". 
Pamiętajcie,  że  referencja  jest  zawsze  odnośnikiem  do  innego,  istniejącego 
obiektu. Jeżeli przekazuje się referencje do lub z funkcji zawsze zadajcie sobie 
pytanie,  czy  obiekt,  do  którego  się  dana  referencja  odnosi,  będzie  nadal 
istniał.

Przykład:

Ten  program  nie  skompiluje 
się  pod  kompilatorami  firmy 
Borland.  Kompilatory  firmy 
Microsoft skompilują ten kod, 
ale trzeba zaznaczyć, że jest 
to błędne podejście.

Funkcja 

deklaruje 

lokalny 

obiekt 

klasy 

ZwyklyKot 

inicjalizuje  jego  wagę  i  wiek. 
Następnie  zwraca  referencje  to 
tego, 

lokalnego 

obiektu. 

Niektóre kompilatory wychwycą 
ten  oczywisty  błąd  i  nie 
pozwolą 

na 

uruchomienie 

programu.  Inne  skompilują  to 
bez 

zastrzeżeń, 

ale 

efekt 

działania takiego programu jest 
nieprzewidywalny.
Kiedy 

Funkcja 

ulegnie 

zakończeniu,  to  lokalny  obiekt, 
oFilemon, zostanie wyrzucony z 
pamięci. Referencja zwrócona z 
funkcji 

odnosiłaby 

się 

do 

nieistniejącego  obiektu  co  jest 
oczywiście 

sprzeczne 

definicją referencji. 

background image

Zwracanie referencji do obiektu na stercie

Można  rozwiązać  stworzony  w  poprzednim  programie  problem  poprzez 
zmianę  Funkcja  tak,  aby  tworzyła  obiekt  klasy  ZwyklyKot  na  stercie.  Dzięki 
temu, po zakończeniu funkcji, taki obiekt będzie nadal istniał.
Pojawia  się  tutaj  kolejny  problem:  Co  zrobić  z  pamięcią  zarezerwowaną  dla 
obiektu oFilemon po zakończeniu wszystkich operacji. 

Ten program się kompiluje i pozornie działa. Jednak jest to 
klasyczna, programistyczna bomba zegarowa z opóźnionym 
zapłonem!

Funkcja  Funkcja  ()  nie  zwraca  już  referencji  do 
lokalnego 

obiektu. 

Teraz 

pamięć 

jest 

rezerwowana  na  stercie  i  przypisywana  do 
wskaźnika. 

Adres 

przechowywany 

przez 

wskaźnik  jest  wypisywany,  a  następnie  są 
zwracane  referencje  do  obiektu  przez  niego 
wskazywanego.

Wartość  zwracana  z  funkcji  jest  przypisywana 
do  referencji  obiektu  klasy  ZwyklyKot.  Obiekt 
ten  wykorzystujemy  do  odczytania  wieku  kota. 
Dowodem na to, że referencja zadeklarowana w 
main  ()  odnosi  się  do  obiektu  stworzonego  na 
stercie jest odczytanie adresu tej referencji. Jest 
on taki sam jak adres wypisany w Funkcja().

 

Ale 

jak 

teraz 

zwolnić 

zarezerwowaną  pamięć?  Nie 
można  skasować  referencji  za 
pomocą 

delete

Jedyne 

nasuwające  się  rozwiązanie 
polega 

na 

zadeklarowaniu 

kolejnego 

wskaźnika 

zainicjowaniu 

go 

adresem 

odczytanym  z  rKot.  W  ten 
sposób 

możemy 

zwolnić 

zarezerwowaną 

pamięć. 

Jednak i tu pojawia się kolejny 
problem.  Do  jakiego  obiektu 
odnosi  się  rKot?  Jak  już 
mówiliśmy,  referencja  musi 
odnosić  się  do  istniejącego 
obiektu.  Jeśli  jest  inaczej 
(obiekt  jest  null)  to  program 
jest nieprawidłowo napisany.

background image

Istnieją trzy rozwiązania tego problemu: 

  Pierwsze  polega  na  zadeklarowaniu  obiektu  klasy  ZwyklyKot  i 
zwrócenie go z funkcji Funkcja () przez wartość.

 Drugie idzie dalej i polega na zadeklarowaniu w funkcji Funkcja () 
obiektu  klasy    ZwyklyKot  na  stercie  i  zwróceniu  wskaźnika  do  tego 
obiektu.  Funkcja  wywołująca  będzie  wtedy  miała  możliwość 
skasowania wskaźnika.

 Trzecie, najbardziej eleganckie, polega na zadeklarowaniu obiektu 
w  funkcji  wywołującej  i  przekazaniu  go  do  funkcji  Funkcja  ()  przez 
referencje.


Document Outline