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

Gestionarea accesului în clasele de bază și derivate

Nivelul de acces și specificatorul protected

Dacă variabilele sau funcțiile dintr-o clasă de bază sunt private, adică declarate cu specificatorul private, atunci clasa derivată, deși moștenește aceste variabile și funcții, nu poate avea acces la ele. De exemplu, să încercăm să definim în clasa derivată o funcție care afișează valorile variabilelor private ale clasei de bază:

#include <iostream>

class Person
{
public:
    Person(std::string name, unsigned age)
    {
        this->name = name;
        this->age = age;
    }
    void print() const
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
private:        // variabile private - accesul din clasa derivată nu este permis
    std::string name;       // nume
    unsigned age;           // vârstă
};

class Employee: public Person
{
public:
    Employee(std::string name, unsigned age, std::string company): Person(name, age)
    {
        this->company = company;
    }
    void printEmployee() const
    {
        std::cout << name << " works in " << company << std::endl;  // ! Eroare
    }
private:
    std::string company;
};

În clasa de bază Person sunt definite variabilele private name și age. În clasa derivată Employee, în funcția printEmployee, încercăm să accesăm aceste variabile pentru a le afișa în consolă. În acest caz vom întâmpina o eroare, deoarece variabilele name și age sunt private în clasa de bază Person, iar clasa derivată Employee nu are acces la ele.

Totuși, uneori apare necesitatea unor variabile sau funcții din clasa de bază care să fie accesibile în clasele derivate, dar să nu fie accesibile din exterior. Adică, ceva similar cu private, dar cu acces permis pentru clasele derivate. Tocmai pentru a defini un astfel de nivel de acces se folosește specificatorul protected.

De exemplu, să definim variabila name cu specificatorul protected:

#include <iostream>

class Person
{
public:
    Person(std::string name, unsigned age)
    {
        this->name = name;
        this->age = age;
    }
    void print() const
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
protected:
    std::string name;   // accesibil din clasele derivate
private:
    unsigned age;
};

class Employee: public Person
{
public:
    Employee(std::string name, unsigned age, std::string company): Person(name, age)
    {
        this->company = company;
    }
    void printEmployee() const
    {
        std::cout << name << " works in " << company << std::endl;
    }
private:
    std::string company;    // companie
};

int main()
{
    Person person {"Tom", 38};
    person.print();     // Name: Tom       Age: 38

    Employee employee {"Bob", 42, "Microsoft"};
    employee.printEmployee();   // Bob works in Microsoft
}

Astfel, putem folosi variabila name în clasa derivată, de exemplu în metoda printEmployee, dar din exteriorul claselor de bază și derivată, accesul la această variabilă nu este posibil.

Nivelul de acces al membrilor clasei de bază

După cum am văzut, specificatorii de acces public, private, protected joacă un rol important în ceea ce privește accesul claselor derivate la variabilele și funcțiile clasei de bază. Totuși, asupra accesului influențează și specificatorul de acces al clasei de bază utilizat în momentul moștenirii:

class Employee: public Person

Astfel, în exemplul de mai sus folosim specificatorul public. Putem folosi de asemenea trei variante: public, protected sau private. Dacă specificatorul clasei de bază nu este indicat în mod explicit:

class Employee: public Person

atunci, în mod implicit, se aplică specificatorul private (în cazul structurilor, dacă specificatorul de acces nu este indicat, atunci implicit se aplică public).

Prin urmare, în clasa de bază, atunci când definim variabile și funcții, putem folosi trei specificatori pentru gestionarea accesului: public, protected sau private. Și aceiași trei specificatori pot fi folosiți și la stabilirea moștenirii din clasa de bază. Acești specificatori se suprapun și formează 9 combinații posibile.

Dacă membrii clasei de bază sunt definiți cu specificatorul private, atunci în clasa derivată aceștia nu sunt accesibili în niciun fel, indiferent de specificatorul de acces utilizat la moștenirea clasei de bază.

Dacă specificatorul de moștenire este public, atunci nivelul de acces al membrilor moșteniți rămâne neschimbat. Astfel, membrii publici moșteniți rămân publici, iar cei moșteniți cu specificatorul protected își păstrează același nivel de acces în clasa derivată.

Dacă specificatorul de moștenire este protected, atunci toți membrii moșteniți care sunt public sau protected în clasa de bază vor deveni protected în clasa derivată. Scopul este ca, dacă și clasa derivată va avea la rândul ei clase derivate, aceste clase să poată accesa acești membri moșteniți.

Dacă specificatorul de moștenire este private, atunci toți membrii moșteniți care erau public sau protected în clasa de bază vor deveni private în clasa derivată. Acești membri vor fi accesibili din interiorul clasei derivate, dar nu și din exterior, inclusiv din clasele care moștenesc această clasă derivată.

Să analizăm un exemplu. Presupunem că folosim private ca specificator de moștenire:

#include <iostream>

class Person
{
public:
    Person(std::string name, unsigned age)
    {
        this->name = name;
        this->age = age;
    }
    void print() const
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
protected:
    std::string name;   // accesibil din clasele derivate
private:
    unsigned age;
};

class Employee: private Person
{
public:
    Employee(std::string name, unsigned age, std::string company): Person(name, age)
    {
        this->company = company;
    }
    void printEmployee() const
    {
        print();    // funcția print este accesibilă în interiorul clasei Employee
        std::cout << name << " works in " << company << std::endl;
    }
private:
    std::string company;    // companie
};

int main()
{
    Employee employee {"Bob", 42, "Microsoft"};
    employee.printEmployee();   // Bob works in Microsoft
    // employee.print();       // funcția print nu este accesibilă
}

Deoarece specificatorul de moștenire față de clasa Person este private, clasa Employee moștenește variabila name și funcția print ca membri private. Acești membri pot fi folosiți în interiorul clasei Employee, dar din exterior nu mai sunt accesibili:

Employee employee {"Bob", 42, "Microsoft"};
// employee.print();       // funcția print nu este accesibilă

Iar dacă creăm o nouă clasă care moștenește clasa Employee, de exemplu o clasă Manager:

class Manager: public Employee
{
public:
    Manager(std::string name, unsigned age, std::string company): Employee(name, age, company)
    { }
};

Atunci variabila name și funcția print din clasa Employee nu vor fi accesibile în clasa Manager.

Stabilirea accesului public

Ce putem face dacă în exemplul de mai sus dorim totuși să apelăm funcția print dintr-un obiect de tip Employee? Putem restabili nivelul de acces folosind cuvântul-cheie using:

#include <iostream>

class Person
{
public:
    Person(std::string name, unsigned age)
    {
        this->name = name;
        this->age = age;
    }
    void print() const
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
protected:
    std::string name;   // accesibil din clasele derivate
private:
    unsigned age;
};

class Employee: private Person
{
public:
    Employee(std::string name, unsigned age, std::string company): Person(name, age)
    {
        this->company = company;
    }
    using Person::print;
    void printEmployee() const
    {
        std::cout << name << " works in " << company << std::endl;
    }
private:
    std::string company;    // companie
};

int main()
{
    Employee employee {"Bob", 42, "Microsoft"};
    employee.print();  // Name: Bob       Age: 42 - funcția este accesibilă
}

În clasa Employee, stabilim ca funcția print a clasei de bază Person să aibă acces public:

using Person::print;

După aceasta, funcția print va avea din nou nivelul de acces inițial — public — și va fi accesibilă din afara clasei Employee:

Employee employee {"Bob", 42, "Microsoft"};
employee.print();    // Name: Bob       Age: 42 - funcția este accesibilă

În mod similar, putem face publică și variabila name, chiar dacă în clasa de bază Person aceasta a fost definită ca protected:

using Person::name;

Totuși, dacă variabilele sau funcțiile sunt definite în clasa de bază ca private, atunci nu le putem face publice în clasa derivată.