De ce contează?
Când suni la o pizzerie, prima dată afli „meniul”: ce poți comanda, cum se numește fiecare produs și ce trebuie să precizezi (mărime, blat). Nu vezi bucătăria, nu vezi cum se face pizza — doar afli că EXISTĂ și cum se cere. Antetul (prototipul) unei funcții este exact acest meniu: îi spune compilatorului ce nume are funcția, ce primește și ce dă înapoi, fără să arate încă „bucătăria” (codul propriu-zis).
Ce este
A declara o funcție înseamnă să anunți compilatorul că funcția există și să-i descrii antetul: tipul valorii returnate, numele și lista de parametri. Declararea se mai numește prototip și se termină cu ; — nu are corp (nu are { }).
A defini o funcție înseamnă altceva: scrii efectiv ce face, în acolade. Definiția conține și ea antetul, dar urmat de corp.
Trei lucruri pe care le anunță prototipul:
- tipul de retur — ce fel de valoare iese din funcție (
int,double,bool, sauvoiddacă nu întoarce nimic) - numele — cum o chemi mai târziu
- lista de parametri — ce primește, cu tipul fiecăruia
Prototipul este o promisiune: „undeva în program va exista o funcție cu acest nume, care primește exact aceste tipuri și întoarce acest tip”. Compilatorul citește programul de sus în jos. Dacă a văzut promisiunea, are voie să o creadă pe cuvânt și să-ți permită să apelezi funcția chiar înainte să fi văzut codul ei.
Cum arată
Iată un prototip tipic — funcția care calculează maximul a două numere:
int maxim(int a, int b);Privește-l ca pe patru bucăți lipite, fiecare cu rolul ei:
int | maxim | (int_int) | ; |
tip-retur | nume | parametri | punct-virgula |
Citește-l așa: „va exista o funcție numită maxim, care primește două numere întregi (a și b) și întoarce un întreg”. Atât promite prototipul — nu spune CUM se calculează maximul, doar că funcția există și ce „formă” are.
Compară cele două forme una lângă alta:
| Formă | Cum arată | Are corp? | Se termină cu |
|---|---|---|---|
| Declarare (prototip) | int maxim(int a, int b); | NU | ; |
| Definire | int maxim(int a, int b) { ... } | DA | } |
În prototip numele parametrilor sunt opționale — compilatorul are nevoie doar de tipuri. Adică int maxim(int, int); este un prototip valid. Le scriem totuși, pentru că ajută cititorul să înțeleagă ce înseamnă fiecare parametru.
De ce există prototipul
Compilatorul procesează fișierul de sus în jos, o singură dată. Când ajunge la un apel maxim(7, 3), trebuie deja să știe că maxim există și ce formă are — altfel se oprește cu eroarea „maxim was not declared in this scope”.
Ai două variante să-l mulțumești:
- pui toată definiția funcției înaintea lui
main— dar atuncimainajunge departe, jos de tot, și programul devine greu de citit; - pui doar prototipul sus (promisiunea),
mainimediat după, iar definiția o lași la urmă.
Prototipul rupe astfel ordinea: poți citi întâi „ce face programul” (main), iar detaliile fiecărei funcții stau dedesubt, ca un apendice.
Implementare C++
Programul de mai jos folosește ordinea „prototip sus, definiție jos”. Observă că main apelează maxim înainte ca definiția lui maxim să apară în fișier — și totuși compilează, datorită prototipului.
#include <iostream>
using namespace std;
// DECLARARE (prototip): doar promisiunea, se termina cu ;
int maxim(int a, int b);
int main() {
int x, y;
cin >> x >> y;
// apelam maxim aici, desi definitia lui apare mai jos.
// compilatorul are voie, pentru ca a vazut prototipul de mai sus.
cout << maxim(x, y) << endl; // pentru 7 si 3 afiseaza 7
return 0;
}
// DEFINIRE: acelasi antet, dar cu corp in acolade
int maxim(int a, int b) {
if (a > b) return a;
return b;
}Dacă ai șterge linia prototipului (a doua), programul NU ar mai compila: la apelul maxim(x, y) compilatorul încă n-a întâlnit nimic numit maxim. Prototipul este exact piesa care îl lasă să continue.
Trei capcane clasice la declarare:
- Uiți punctul și virgula după prototip:
int maxim(int a, int b)(fără;) nu mai e o declarare — compilatorul crede că urmează un corp și se încurcă la linia următoare. Prototipul se termină ÎNTOTDEAUNA cu;. - Tipurile din prototip nu se potrivesc cu definiția. Dacă declari
int maxim(int, int);dar defineștidouble maxim(int a, int b) { ... }, ai descris de fapt DOUĂ funcții diferite. Antetul din prototip și cel din definiție trebuie să aibă același tip de retur și aceleași tipuri de parametri, în aceeași ordine. - Declari, dar nu definești niciodată. Dacă pui prototipul și apelezi funcția, dar uiți să-i scrii corpul, compilarea „de sus” trece, însă la final apare o eroare de linker: „undefined reference to
maxim”. Promisiunea există, dar funcția promisă lipsește. O declarare fără o definiție corespunzătoare este o promisiune neonorată.