Funkcije


Ogledali si bomo nekaj izboljšav, ki jih pri funkcijah vnaša  C++ (pa tudi C). Z uvedbo prototipov omogočimo prevajalniku dodatno preverjanje naših programov.

PROTOTIPI

Primer programa ------> PROTYPE1.CPP

Oglejmo si ponazoritev prototipa v datoteki  PROTYPE1.CPP. Oporaba prototipov v  C++ je enaka kot pri ANSI-C.

Prototip je omejen model bolj zapletene entitete, ki jo izpopolnimo kasneje . V našem primeru je prototip funkcije v vrstici  4. Prototip podaja model vmesnika, kako in s kakšnimi parametri bomo klicali napovedano funkcijo. Vsak klic funkcije  do_stuff() mora imeti natančno tri parametre, sicer bo prevajalnik javil napako. Parametri se morajo ujemati tako po številu kot po tipu.  Če prototipa funkcije ne podamo, prevajalnik ne bo preverjal števila in tipa parametrov pri klicih take funkcije. Zato bo morda prevod programa brez opozoril, rezultati pri izvedbi programa pa bodo napačni.

Prototip napišemo tako, da preprosto kopiramo glavo funkcije na začetek programa in ga zaključimo  s podpičjem. Imena spremenljivk v prototipu lahko sicer navedemo, vendar jih v resnici prevajalnik ignorira.
 

KOMPATIBLNI TIPI

Kompatibilni tipi so vsi preprosti tipi, ki jih lahko medsebojno  smiselno pretvarjamo. Tako lahko imamo nek celoštevilčni parameter, funkcija pa morda formalno pričakuje parameter tipa  float. Sistem bo sam avtomatsko opravil konverzijo. Enako velja za spremembo iz float v char ali iz char int.

Če je kot formalni parameter funkcije naveden int, v klicu funkcije pa navedemo kazalec na int, do konverzije ne more priti, saj sta to dve popolnoma različni stvari. Podobno tudi ne bi prišlo do pretvorbe neke strukture v   long float,  polje  alo morda celo v neko drugo obliko strukture, saj je smiselna pretvorba nemogoča. Podobno velja tudi za vračani tip funkcije (v našem primeru void), ki se mora ujemati z napovedanim oziroma pričakovanim tipom.
 

KAKO DELUJEJO PROTOTIPI?

Delovanje prototipov najlažje spoznamo s poskušanjem. Spremenimo aktualne parametre v vrstici  12 v (12.2, 13, 12345) in poglejmoč kaj poreče prevajalnik. Verjetno nič, ker so parametri kompatibilni. Opozorilo o napaki pa dobimo, če spremenimo parametre na   (12.0, 13), ker jih nismo navedli dovolj. Napaka bi tudi bila, če bi pred enega od imen spremenljivk v vsrtici 13 dali znak  &, s čemer bi na tem mestu navedli naslov spremenljivke. Tudi sprememba  void v int  v vrstici 4 bi povzročila izpis opozorila o napaki. Po popravku glave funkcije v vrstici 18 tako, da se bo ponovno ujemala s prototipom,  to opozorilo izgine, zato pa ugotovimo, da nam funkcija ne vrača nič, kar pa prevajalniku spet ni všeč. Vidimo, da nam prototipi pomagajo pri iskanju napak v naših programih.

Primer programa ------> PROTYPE2.CPP

Poglejmo si sedaj naslednji primer v datoteki  PROTYPE2.CPP. Program je podoben prejšnjemu. Imena spremenljivk v prototipu v vrstici 4 so izpuščena in tako spoznamo, da jih prevajalnik pravzaprav obravnava kot komentar.
 

POSREDOVANJE Z REFERENCO

Primer programa ------> PASSREF.CPP

Program PASSREF.CPP kaže posredovanje z referenco, kar pri ANSI-C ni mogoče. Referenčno spremenljivko smo že prej spoznali, ta primer pa kaže njeno uporabnost. Posredovanje z referenco omogoča prenos spremenljivke funkciji tako, da bodo njene spremembe opazne tudi v kličočem programu. Pri ANSI-C smo isto dosegli s posredovanjem kazalca na spremenljivko, vendar je ta metoda bolj jasna.

Prototip v vrstici 5 ima sedaj pred drugo spremenljivko znak &. Ta znak pove prevajalniku, da naj to spremenljivko obravnava kot referenco na resnično spremenljivko, ki jo posreduje kličoči program.  V sami funkciji (vrstice 24 do 27)  uporabljamo spremenljivko  in2, ta poa se obnaša tako, kot bi  uporabljali spremenljivko iz kličočega programa, ne pa njeno kopijo. Druga spremenljivka,   in1  pa se obnaša tako, kot smo navajeni pri  ANSI-C. Ime in2 je sinonim za spremenljivko z imenom   index  v glavnem programu, ime   in1 pa velja za kopijo spremenljivke count glavnega programa.

V prototipu funkcije lahko imena spremenljivk tudi izpustimo in dobimo:

    void fiddle(int, int&);
Pascalski programerji spoznajo in1 kot normalen parameter v klicu funkcije, klican po vrednosti. Spremenljivko  in2 pa obravnavamo kot spremenljivko ki ima  spredaj besedico VAR, čemur pravimo klic po referenci.

Ob pridobivanju programerskih izkušenj spoznamo vso učinkovitost posredovanja z referenco, še posebno, če poramo funkcijam posredovati kar podatkovne strukture (saj zadošča posredovanje njihovega naslova).
 

PRIVZETI PARAMETRI

Primer programa ------> DEFAULT.CPP

Program  DEFAULT.CPP kaže uporabo privzetih (default) parametrov pri C++.  Prototip funkcije pove, da moramo pri vsakem klicu obvezno podati prvi parameter z imenom  length, saj ni podana njegova privzeta (default) vrednost. Drugi parameter z imenom  width pa ni obvezen pri vsakem klicu funkcije. Če ga ne navedemo, bo imela spremenljivka  width znotraj funkcije vrednost 2. Podobno ni obvezen tretji parameter. Če ga ne navedemo, bo vrednost spremenljivke  height v funkciji enaka 3.

V vsrtici 11 so navedeni vsi trije parametri in to ni nič nenavadnega. V vrstici 12 manjka navedba trethega parametra, zato se bo program obnašal tako, kot če bi klicali funkcijo  get_volume(x,y,3). V vrstici 13 manjkata celo dva parametra. Tako se bo program obnašal, kot če bi uporabili klic  get_volume(x,2,3).

Upoštevati moramo nekaj pravil. Če je za nek parameter v seznamu parametrov uporabljena privzeta vrednost, bodo uporabljene privzete vrednosti tudi za vse naslednje parametre, saj ne moremo imeti "luknje" v seznamu. Seveda mora biti za privzete vrednosti uporabljen pravi tip. Privzete vrednosti lahko navedemo bodisi v prototipu, bodisi v glavi funkcije (ne pa v obeh hkrati). Stilistično je pametneje, če jih navajamo v prototipu funkcije.
 

SPREMENLJIVO ŠTEVILO ARGUMENTOV

Primer programa ------> VARARGS.CPP

Program VARARGS.CPP kaže primer s spremenljivim številom argumentov v funkcijskem klicu. V nekaterih primerih si to želimo, spomnimo se na primer funckije  printf(). ANSI-C rešuje to z več makroji v zaglavni datoteki  "stdarg.h". To sicer lahko uporabimo tudi v  C++, radi pa bi poenostavili preverjanje tipa parametrov. To naredi za nas zaporedje treh točk v vrstici 6.  Prototip v našem primeru zahteva, da je prvi argument tipa  int is, morebitnih ostalih pa prevajalnik ne bo preverjal .
Seveda lahko taka poenostavitev dovoli tvorbo nenavadne programske kode, in se ji izogibajmo.
 

PREKRIVANJE IMEN FUNKCIJ

Primer programa ------> OVERLOAD.CPP

Program  OVERLOAD.CPP kaže prekrivanje (preobremenjevanje, overloading) več imen funkcij. To izgleda nenavadno, vendar je ključnega pomena za objektno usmerjeno programiranje.

Imamo tri funkcije z enakim imenom. Katera funkcija bo torej poklicana ob izvedbi klica  do_stuff()?"  Klicana bo funkcija, pri kateri se z dejanskimi parametri ujemata tako število kot tudi tip formalnih parametrov. Če je v klicu  do_stuff() navedeno kot parameter le eno celo število,  bo klicana funkcija, ki začne v vrstici  25. Če je naveden en sam  aktualni parameter tipa  float, bo klicana funkcija z začetkom v vrstici 30. Če navedemo dva parametra tipa float, kličemo funkcijo v vrstici  36.

Na izbiro prekrivane funkcije vpliva le število in tip formalnih parametrov, ne pa tip vračanega rezultata funkcije.

Ključna beseda overload (vrstica 4) ni obvezujoča, napove pa našo namero definiranja več prekrivanih funkcij z imenom do_stufft. Pri novejših programih jo običajno opuščamo.

Katera od prekritih funkcij bo uporabljena, določi že prevajalnik, zato program v zasu izvajanja ni nič upočasnjen.



Kazalo   Enkapsulacija