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

Obiecte și funcții constante

Obiecte constante

Obiectele claselor pot, de asemenea, reprezenta constante:

#include <iostream>
  
class Person 
{
public:
    std::string name;
    unsigned age;
    Person(std::string p_name, unsigned p_age)
    {
        name = p_name;
        age = p_age;
    }
};
int main()
{
    const Person tom{"Tom", 38};
    // putem obține datele constantei
    std::cout << "Name: " << tom.name << "\tAge: " << tom.age << std::endl;
    // dar nu le putem modifica
    // tom.name = "Tom";    // ! Eroare
    // tom.age = 38;        // ! Eroare
}

În cazul lucrului cu obiecte constante, putem obține valorile câmpurilor lor, dar nu le putem modifica. Astfel, dacă în exemplul de mai sus decomentăm linia:

tom.name = "Tom"; // ! Eroare

vom obține o eroare la compilare, deoarece obiectul tom este constant.

Funcțiile unui obiect constant

Constanța obiectului impune anumite restricții asupra apelului funcțiilor sale. De exemplu, să adăugăm funcția print() în clasa Person:

#include <iostream>
  
class Person 
{
private:
    std::string name;
    unsigned age;
public:
    Person(std::string p_name, unsigned p_age)
    {
        name = p_name;
        age = p_age;
    }
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
};
int main()
{
    const Person tom{"Tom", 38};
    tom.print();    // ! Eroare
}

Surprinzător, acest exemplu nu va fi compilat, deși în funcția print nu modificăm câmpurile obiectului. Asta deoarece orice funcție a unei clase poate, teoretic, modifica starea obiectului, iar compilatorul nu poate ști dacă o funcție modifică ceva sau nu. Prin urmare, refuză compilarea atât a funcțiilor care modifică, cât și a celor care nu modifică obiectul.

Pentru un obiect constant se pot apela doar funcții constante. Pentru a defini o funcție constantă, se adaugă cuvântul cheie const după lista parametrilor:

#include <iostream>
  
class Person 
{
private:
    std::string name;
    unsigned age;
public:
    Person(std::string p_name, unsigned p_age)
    {
        name = p_name;
        age = p_age;
    }
    // funcție constantă
    void print() const 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
};
int main()
{
    const Person tom{"Tom", 38};
    tom.print();            // Name: Tom       Age: 38
    Person bob{"Bob", 42};
    bob.print();            // Name: Bob       Age: 42
}

Aici, funcția print este declarată ca fiind constantă, deci poate fi apelată atât pentru un obiect constant, cât și pentru unul neconstant. În orice caz, într-o funcție constantă nu trebuie să modificăm câmpurile clasei.

Apelul altor funcții dintr-o funcție constantă

O altă restricție este că o funcție constantă poate apela doar alte funcții constante din aceeași clasă:

#include <iostream>
  
class Person 
{
private:
    std::string name;
    unsigned age;
public:
    Person(std::string p_name, unsigned p_age)
    {
        name = p_name;
        age = p_age;
    }
    std::string getName() const
    {
        return name;
    }
    unsigned getAge() const
    {
        return age;
    }
    void print() const 
    {
        // în funcțiile constante putem apela doar funcții constante
        std::cout << "Name: " << getName()  << "\tAge: " << getAge() << std::endl;
    }
};
int main()
{
    const Person tom{"Tom", 38};
    tom.print();            // Name: Tom       Age: 38
    Person bob{"Bob", 42};
    bob.print();            // Name: Bob       Age: 42
}

Aici au fost definite funcțiile getName și getAge, care returnează numele și vârsta. Ambele sunt funcții constante, așa că pot fi apelate din funcția constantă print.

Returnarea constantelor

O altă limitare legată de funcțiile constante este că dacă vrem să returnăm un pointer sau o referință, acestea trebuie să fie către constante. Să vedem cum se manifestă acest lucru:

#include <iostream>
  
class Person 
{
private:
    std::string name;
    unsigned age;
public:
    Person(std::string p_name, unsigned p_age)
    {
        name = p_name;
        age = p_age;
    }
    // returnăm o referință constantă
    const std::string& getName() const
    {
        return name;
    }
    // returnăm un pointer către constantă
    const unsigned* getAge() const
    {
        return &age;
    }
    void print() const 
    {
        std::cout << "Name: " << name  << "\tAge: " << age << std::endl;
    }
};
int main()
{
    const Person tom{"Tom", 38};
 
    std::string tom_name = tom.getName();
    const unsigned* tom_age = tom.getAge();
 
    std::cout << "Name: " << tom_name  << "\tAge: " << *tom_age << std::endl;
}

Aici, funcția constantă getName returnează o referință constantă, iar getAge returnează un pointer către o constantă.

Cuvântul cheie mutable

Uneori este necesar ca anumite date ale unui obiect constant să poată fi totuși modificate. În acest caz, putem folosi cuvântul cheie mutable pentru variabila care trebuie să rămână modificabilă. Chiar dacă obiectul este constant, valoarea acestei variabile poate fi schimbată:

#include <iostream>
  
class Person 
{
public:
    std::string name;
    mutable unsigned age;   // variabila age poate fi modificată
    Person(std::string p_name, unsigned p_age)
    {
        name = p_name;
        age = p_age;
    }
    void print() const 
    {
        std::cout << "Name: " << name  << "\tAge: " << age << std::endl;
    }
};
int main()
{
    const Person tom{"Tom", 38};
    tom.age = 22;
    tom.print();    // Name: Tom       Age: 22
}