Static și Operatori

Static

Cu ajutorul keywordului static putem declara variabile care țin de clasă, nu de obiect. Funcționează similar cu o variabilă globală, doar că izolat pentru o clasă.

class Employee {
private:
    static uint32_t employeeCount;
    uint32_t employeeID;
public:
    Employee() {
        employeeCount++;
        employeeID = employeeCount;
    }
    
    uint32_t getEmployeeID() {
        return employeeID;
    }
};

uint32_t Employee::employeeCount = 0;

int main() {
    Employee employee1;
    Employee employee2;
    Employee employee3;
    std::cout << employee1.getEmployeeID() << '\n'; // 1
    std::cout << employee2.getEmployeeID() << '\n'; // 2
    std::cout << employee3.getEmployeeID() << '\n'; // 3
    return 0;
}

Din păcate, membrii statici trebuie inițializați în afara clasei după ce sunt declarați în clasă. Similar, putem declara și metode statice. Dacă adăugăm la clasa de mai devreme:

static void getEmployeeCount() {
    return employeeCount;
}

secvența următoare se va comporta astfel:

Employee employee1;
Employee employee2;
Employee employee3;
std::cout << employee1.getEmployeeCount() << '\n'; // 3
std::cout << Employee::getEmployeeCount() << '\n'; // 3

Cele 2 apeluri sunt același lucru. Atât membri statici, cât și metodele staticve pot fi apelate de pe clasă, fără un obiect, dar și de pe orice obiect.

Operatori

Putem supraîncărca majoritatea operatorilor uzuali în C++.

class Vector2 {
private:
    float x;
    float y;
    
public:
    Vector2(float x, float y) {
        this->x = x;
        this->y = y;
    }

    Vector2 operator +(const Vector2 &other) const {
        return Vector2(x + other.x, y + other.y);
    }
    
    Vector2 &operator =(const Vector2 &other) {
        x = other.x;
        y = other.y;
        return *this;
    }
    
    // Prefix
    Vector2 operator ++() {
        ++x;
        ++y;
        return *this;
    }
    
    // Postfix
    Vector2 operator ++(int) {
        Vector2 old(x, y);
        ++x;
        ++y;
        return old;
    }
    
    friend std::istream &operator >> (std::istream &is, Vector2 &vec) {
        is >> vec.x >> vec.y;
        return is;
    }
    
    friend std::ostream &operator << (std::ostream &os, const Vector &vec) {
        os << vec.x << ' ' << vec.y;
        return os;
    }
};

Cel mai intuitiv operator de supraîncărcat este +. constul de dinainte de { stabilește ca obiectul curent nu poate fi modificat. Un exemplu puțin mai neobișnuit este =, la care trebuie avută puțină grijă dacă vrem să funcționeze o instrucțiune de forma a = b = c. Aici, se execută b = c care întoarce o referință la b, după care se continuă cu a = b astfel încât toate devin egale cu c la final.

Operatorii de incrementare și decrementare sunt mai neobișnuiți. Fiind același operator atât la preincrementare cât și la postincrementare, se diferențază prin faptul ca postincrementarea are un parametru de tip int inutil (de asta nici nu îi dăm nume).

Operatorii de citire și afișare sunt iarăși mai ciudați din cauza friend. Ce se întâmplă, de fapt, acolo, este că supraîncărcările operatorilor << și >> nu fac parte din clasă. friend denotă ca o funcție are acces la membrii privați ai clasei (astfel încât să putem citi și afișa). Operatorii întorc o referință la un stream după ce acesta este modificat cu ceea ce citim/afișăm pentru a putea fi înlănțuite. Cu implementarea de mai sus, funcționează corect std::cin >> a >> b unde a și b sunt de tip Vector2.

Exerciții

  1. Creați clasa Complex care modelează un număr complex și supraîncărcați toți operatorii care au sens din punct de vedere matematic și >>, <<, preincrementare, postincrementare.