MySQL Java JavaScript PHP Python HTML-CSS C-sharp C++ Go

Controlul accesului - Încapsularea

Clasa poate defini diverse stări, funcții diferite. Totuși, nu este întotdeauna de dorit ca anumite componente ale clasei să fie accesibile direct din exterior. Pentru a delimita accesul la diferitele componente ale unei clase, se folosesc specificatori de acces.

Specificatorul public face ca membrii clasei – câmpurile și funcțiile – să fie deschiși, accesibili din orice parte a programului. De exemplu, să luăm următoarea clasă Person:

#include <iostream>
 
class Person 
{
public:
    std::string name;
    unsigned age;
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        age = p_age;
    }
};
int main()
{
    Person tom{"Tom", 38};
    // câmpurile name, age și funcția print sunt publice
    tom.name = "Tomas"; 
    tom.age = 22;
    tom.print();    // Name: Tomas   Age: 22
}

În acest caz, câmpurile name și age și funcția print sunt deschise, publice, și putem accesa aceste elemente din codul extern. Totuși, acest lucru are anumite dezavantaje. De exemplu, putem accesa câmpurile clasei și să le atribuim orice valoare, chiar dacă ele nu sunt corecte din punctul de vedere al logicii programului:

Person tom("Tom", 22);
tom.name = "";
tom.age = 1001;

Inclusiv se pot atribui valori nepermise. De exemplu, câmpului age i se poate atribui o vârstă exagerat de mare, ireală. Sau nu dorim ca numelui să i se poată atribui un șir gol. Evident, aceasta nu este o situație prea bună.

Totuși, cu ajutorul altui specificator, private, putem ascunde implementarea membrilor clasei, adică să-i facem închiși, încapsulați în interiorul clasei. Să rescriem clasa Person folosind specificatorul private:

#include <iostream>
 
class Person 
{
private:
    std::string name;
    unsigned age;
public:
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        age = p_age;
    }
};
int main()
{
    Person tom{"Tom", 38};
    // funcția print este publică
    tom.print();    // Name: Tom   Age: 22
 
    // câmpurile name și age nu sunt accesibile din afara clasei
    // tom.name = "";  
    // tom.age = 1001;
}

Toate componentele definite după specificatorul private și până la public sunt închise, private. Acum nu mai putem accesa variabilele name și age din afara clasei Person. Putem accesa aceste variabile doar în interiorul clasei. Însă funcția print și constructorul rămân publice, așa că le putem folosi în orice parte a programului.

Este de remarcat că și în acest caz putem transmite valori incorecte – prin constructor. În acest caz, putem verifica datele de intrare și aplica diverse strategii, de exemplu să nu creăm obiectul sau să transmitem date implicite, dar pentru simplificare nu vom include astfel de verificări.

Dacă pentru anumite componente nu este specificat un specificator de acces, atunci implicit se aplică private. Astfel, clasa Person anterioară este echivalentă cu următoarea:

class Person 
{
    std::string name;
    unsigned age;
public:
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        age = p_age;
    }
};

Medierea accesului

Deși în exemplul de mai sus evităm atribuirea valorilor incorecte, uneori totuși poate fi necesar accesul la astfel de câmpuri. De exemplu, persoana a mai îmbătrânit cu un an – trebuie să-i modificăm vârsta. Sau dorim să obținem numele separat. În acest caz, putem defini funcții speciale, prin care vom controla accesul la starea clasei:

#include <iostream>
 
class Person 
{
private:
    std::string name;
    unsigned age;
public:
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        if (p_age > 0 && p_age < 110) 
            age = p_age;
        else
            age = 18;   // dacă valoarea este incorectă, setăm valoarea implicită
    }
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
    void setAge(unsigned p_age) 
    {
        if (p_age > 0 && p_age < 110) 
            age = p_age;
    }
    std::string getName()
    {
        return name;
    }
    unsigned getAge()
    {
        return age;
    }
};
int main()
{
    Person tom{"Tom", 38};
    // modificăm vârsta
    tom.setAge(22);
    tom.setAge(123);
    tom.print();    // Name: Tom   Age: 22
 
    // obținem separat numele
    std::cout << "Person name: " << tom.getName() << std::endl;
}

Pentru a putea obține din exterior valorile variabilelor name și age, au fost definite funcțiile suplimentare getAge și getName. Valoarea variabilei name poate fi setată doar prin constructor, iar valoarea variabilei age – prin constructor sau prin funcția setAge. În același timp, funcția setAge setează valoarea variabilei age doar dacă aceasta respectă anumite condiții.

Astfel, starea clasei este ascunsă din exterior, iar accesul la aceasta este posibil doar prin funcții special definite, care formează interfața clasei.