Domeniul de vizibilitate al obiectelor
Toate variabilele au o anumită durată de viață (lifetime) și un domeniu de vizibilitate (scope). Durata de viață începe din momentul definirii variabilei și durează până la distrugerea acesteia. Domeniul de vizibilitate reprezintă partea programului în care obiectul poate fi utilizat. De regulă, domeniul de vizibilitate este limitat de un bloc de cod încadrat între acolade. În funcție de domeniul de vizibilitate, obiectele create pot fi globale, locale sau automate.
Obiecte globale
Variabilele globale sunt definite în fișierul programului în afara oricărei funcții sau a oricărui alt bloc de cod și pot fi folosite de orice funcție. Variabilele globale există pe întreaga durată de viață a programului și sunt distruse doar la terminarea acestuia.
Dacă variabilele globale nu sunt inițializate, ele primesc valori nule.
De exemplu, să definim și să folosim o variabilă globală:
#include <iostream>
int n{5}; // variabilă globală
void print()
{
n++;
std::cout << "n=" << n << std::endl;
}
int main()
{
print(); // n=6
n++;
std::cout << "n=" << n << std::endl; // n=7
}
Aici variabila n este globală și accesibilă din orice funcție. Orice funcție îi poate modifica valoarea.
Obiecte locale
Obiectele create în interiorul unui bloc de cod (poate fi o funcție sau o construcție de tip ciclu) se numesc locale. Astfel de obiecte sunt accesibile doar în cadrul acelui bloc în care au fost definite.
Obiecte automate
Obiectele locale care există doar în timpul execuției acelui bloc în care au fost definite sunt automate.
La intrarea în bloc se alocă memorie pentru aceste variabile, iar după terminarea execuției blocului, memoria este eliberată și obiectele sunt distruse.
#include <iostream>
void print()
{
int n {5}; // variabilă locală, care există doar în funcția print
std::cout << "n=" << n << std::endl;
// nu se poate, deoarece m este definită în funcția main
// std::cout << "m=" << m << std::endl;
}
int main()
{
int m {2}; // variabilă locală, care există doar în funcția main
std::cout << "m=" << m << std::endl;
// nu se poate, deoarece n este definită în funcția print
// std::cout << "n=" << n << std::endl;
}
Aici, în funcția print este definită variabila locală n. În funcția main este definită variabila automată m. În afara funcțiilor lor, aceste variabile nu sunt accesibile. De exemplu, nu putem folosi variabila n în funcția main, deoarece domeniul ei de vizibilitate este limitat la funcția print. În mod similar, nu putem folosi variabila m în funcția print, deoarece ea este limitată la funcția main.
În mod similar, putem defini domenii de vizibilitate imbricate folosind un bloc de cod:
#include <iostream>
int main()
{
int n1 {2}; // domeniul de vizibilitate - întreaga funcție main
{
int n2 {5}; // domeniul de vizibilitate - blocul de cod
std::cout << "n2=" << n2 << std::endl;
n1++; // variabila n1 este disponibilă deoarece este definită în contextul extern - funcția
} // sfârșitul blocului - sfârșitul duratei de viață a lui n2
// nu se poate - variabila n2 nu mai există
// std::cout << "n2=" << n2 << std::endl;
// n1 este disponibilă până la finalul funcției
std::cout << "n1=" << n1 << std::endl;
} // sfârșitul blocului - sfârșitul duratei de viață a lui n1
Pentru fiecare domeniu de vizibilitate sunt disponibile toate obiectele definite în domeniul extern. Domeniul global este extern pentru funcție, deci funcția poate utiliza variabile globale. Iar funcția este context extern pentru blocul de cod, deci blocul poate folosi variabila n1, definită în funcție, în afara blocului. Totuși, variabilele definite în bloc nu pot fi folosite în afara lui.
Ascunderea obiectelor
Obiectele locale definite într-un context pot ascunde obiectele cu același nume definite în contextul exterior:
#include <iostream>
int n {5};
int main()
{
int n {10};
std::cout << "n=" << n << std::endl; // n=10
{
int n {20};
std::cout << "n=" << n << std::endl; // n=20
}
}
Aici sunt definite trei variabile cu numele n. Variabila n definită în main (int n = 10;) ascunde variabila globală n. Iar variabila n definită în blocul intern ascunde variabila n definită în main.
Totuși, uneori este necesar să accesăm variabila globală. În acest caz, pentru a face referire exact la variabila globală, se poate folosi operatorul :: înaintea numelui:
#include <iostream>
int n {5};
int main()
{
int n {10};
std::cout << "n=" << ::n << std::endl; // n=5
{
int n {20};
std::cout << "n=" << ::n << std::endl; // n=5
}
}
Obiecte statice
Pe lângă cele automate, există un tip special de obiecte locale — obiectele statice. Ele sunt definite în cadrul funcțiilor cu ajutorul cuvântului cheie static. Dacă variabilele automate sunt definite și inițializate la fiecare apel al funcției, variabilele statice sunt inițializate o singură dată, iar la apelurile ulterioare se folosește valoarea anterioară. Diferența dintre variabilele locale automate și cele statice constă în durata de viață: variabilele automate trăiesc până la finalul blocului de cod, iar cele statice — până la sfârșitul programului.
De exemplu, să avem o funcție cu o variabilă automată:
#include <iostream>
void print()
{
int n {1};
std::cout << "n=" << n << std::endl;
n++;
}
int main()
{
print();
print();
print();
}
Funcția print este apelată de trei ori, și la fiecare apel programul va aloca din nou memorie pentru variabila n. După finalizarea execuției funcției print, memoria variabilei n este eliberată. Astfel, valoarea sa va fi mereu aceeași:
n=1
n=1
n=1
Acum să facem variabila n statică:
#include <iostream>
void print()
{
static int n {1};
std::cout << "n=" << n << std::endl;
n++;
}
int main()
{
print();
print();
print();
}
A fost adăugat cuvântul cheie static variabilei, așa că după terminarea execuției funcției print, variabila nu este distrusă, iar memoria ei nu este eliberată. Dimpotrivă, ea este păstrată în memorie. Astfel, rezultatul execuției programului va fi diferit:
n=1
n=2
n=3