POO Cauta

Saptamana 03 / 14 Programare Orientata Obiect

Introducere in clase

De la struct la class. Cum definim o clasa cu atribute si metode, ce tipuri de metode exista, cum cream obiecte si cum protejeaza public si private starea unui obiect.

Obiective
  • Sa compari struct, union si class
  • Sa definesti o clasa cu atribute si metode
  • Sa definesti o metoda in clasa si in afara ei, cu operatorul de rezolutie `::`
  • Sa deosebesti tipurile de metode (de acces, de prelucrare, auxiliare, const)
  • Sa creezi obiecte si sa explici ca fiecare obiect are propriile atribute
  • Sa folosesti public, private si protected si sa stii accesul implicit in struct si class

De ce nu ne ajunge un struct

In saptamana 2 am grupat date cu struct:

struct Student {
    int varsta;
    string nume;
    int* note;
};

Student s;
s.varsta = -40;          // compileaza, iar datele nu mai au sens

Oricine poate scrie orice in s.varsta sau s.note. Un struct grupeaza datele, dar nu le protejeaza. O clasa grupeaza datele si operatiile permise asupra lor si poate ascunde datele, astfel incat doar acele operatii sa le poata modifica.

struct, union, class

MembriAcces implicitFolosire tipica
structdate si functii, fiecare cu memoria luipublicgrupuri simple de date
classdate si functii, fiecare cu memoria luiprivatetipuri cu reguli
uniontoate datele folosesc aceeasi memoriepublico valoare dintre mai multe tipuri

Intr-un union este valid un singur membru la un moment dat: ultimul in care ai scris.

union Valoare {
    int i;
    double d;
};

Valoare v;
v.i = 42;      // i este membrul activ
v.d = 3.5;     // acum d este activ; de aici incolo nu mai citim v.i

Citirea altui membru decat ultimul scris este comportament nedefinit (undefined behaviour) in C++. La acest curs vei folosi rar union. Il prezentam ca sa stii ce este.

Definirea unei clase

O clasa are atribute (date membre, adica starea) si metode (functii membre, adica comportamentul).

class ContBancar {
    double sold = 0;                    // atribut (private implicit)
public:
    void depune(double suma) {          // metoda definita in clasa
        if (suma > 0) sold += suma;
    }
    bool retrage(double suma);          // doar declarata aici
    double getSold() const { return sold; }
};                                      // nu uita ';'

bool ContBancar::retrage(double suma) { // definita in afara, cu ::
    if (suma <= 0 || suma > sold) return false;
    sold -= suma;
    return true;
}

ContBancar::retrage se citeste “functia retrage care apartine clasei ContBancar”. Fara ContBancar:: ar fi o functie oarecare, care nu stie nimic despre sold.

Un bool se afiseaza ca 1 sau 0. Ce afiseaza programul?

#include <iostream>
using namespace std;
class ContBancar {
    double sold = 0;
public:
    void depune(double suma) { if (suma > 0) sold += suma; }
    bool retrage(double suma);
    double getSold() const { return sold; }
};
bool ContBancar::retrage(double suma) {
    if (suma <= 0 || suma > sold) return false;
    sold -= suma;
    return true;
}
int main() {
    ContBancar cont;
    cont.depune(100);
    cont.depune(-50);
    bool ok1 = cont.retrage(30);
    bool ok2 = cont.retrage(500);
    cout << ok1 << " " << ok2 << " " << cont.getSold();
}

Tipuri de metode

  • Metode de acces citesc sau modifica un atribut in mod controlat: getSold(). Un setter, daca exista, verifica valoarea primita.
  • Metode de prelucrare fac munca reala a obiectului: depune, retrage. Ele sunt motivul pentru care exista clasa.
  • Metode auxiliare sunt private. Le folosesc alte metode si nu fac parte din interfata publica.
  • Metode const promit ca nu modifica obiectul: double getSold() const. Compilatorul verifica promisiunea.
  • Constructorii si destructorul sunt metode speciale. Ii discutam saptamana viitoare.

O metoda poate fi inline (definita in corpul clasei) sau definita in afara, cu ::. Comportamentul este acelasi. Metodele lungi se scriu de obicei in afara, ca sa ramana clasa usor de citit.

Obiecte

O clasa este un tip. Un obiect este o variabila de acel tip. Fiecare obiect are propria copie a atributelor. Metodele exista o singura data si lucreaza pe obiectul pentru care sunt apelate.

#include <iostream>
using namespace std;
class Contor {
    int valoare = 0;
public:
    void creste() { valoare++; }
    int get() const { return valoare; }
};
int main() {
    Contor a, b;
    a.creste();
    a.creste();
    b.creste();
    cout << a.get() << " " << b.get();
}

In interiorul unei metode, this este un pointer catre obiectul curent. valoare++ din a.creste() inseamna de fapt this->valoare++, cu this == &a.

Obiectele traiesc acolo unde traieste orice variabila: obiectele locale pe stiva, cele create cu new pe heap.

STIVA
a.valoare: 0
b.valoare: 0
HEAP

Contor a, b; - doua obiecte pe stiva, fiecare cu propria valoare.

Pasul 1/4

Domenii de vizibilitate (specificatori de acces)

SpecificatorCine poate folosi membrul
publicoricine
privatedoar metodele clasei (si prietenii ei, saptamana 6)
protectedclasa si clasele derivate din ea (saptamana 8)

Accesul este verificat de compilator. Codul urmator este gresit intentionat:

ContBancar cont;
cont.sold = 1000000;   // eroare de compilare: 'double ContBancar::sold' is private within this context

Incapsularea inseamna: atributele sunt private, iar metodele publice pastreaza obiectul valid. O regula care trebuie sa fie mereu adevarata pentru un obiect, de exemplu “soldul nu este niciodata negativ”, se numeste invariant. Daca doar metodele pot modifica starea, invariantul trebuie verificat doar in metode.

Ruleaza programul de mai jos. Apoi scoate comentariul de pe ultima linie si citeste mesajul compilatorului.

#include <iostream>
using namespace std;

struct PunctS {        // struct: membrii sunt public implicit
  int x = 0, y = 0;
};

class Dreptunghi {     // class: membrii sunt private implicit
  double latime = 1, inaltime = 1;
public:
  bool setDimensiuni(double l, double h) {
      if (l <= 0 || h <= 0) return false;   // dreptunghiul ramane valid
      latime = l;
      inaltime = h;
      return true;
  }
  double arie() const { return latime * inaltime; }
};

int main() {
  PunctS p;
  p.x = 3;                       // permis: public
  cout << "punct: " << p.x << " " << p.y << endl;

  Dreptunghi d;
  cout << "arie: " << d.arie() << endl;
  d.setDimensiuni(2, 5);
  cout << "arie: " << d.arie() << endl;
  cout << "acceptat: " << d.setDimensiuni(-1, 4) << endl;
  cout << "arie: " << d.arie() << endl;
  // d.latime = -3;              // incearca: eroare de compilare, latime e private
}
Intrare (stdin)

Deschide in Godbolt

Verifica-te

1. Ce acces au implicit membrii declarati fara specificator?

2. sold este un atribut private al clasei ContBancar. Ce se intampla cu cont.sold = 5; scris in main?

3. In interiorul metodei double getSold() const, care dintre urmatoarele sunt permise?

4. Ce afiseaza programul?
#include <iostream>
using namespace std;
struct P { int x = 1; };
class Q {
    int x = 2;
public:
    int get() const { return x; }
};
int main() {
    P p;
    Q q;
    p.x = 5;
    cout << p.x << " " << q.get();
}

5. Ce afiseaza programul?
#include <iostream>
using namespace std;
class Cutie {
    int marime = 1;
public:
    void setMarime(int marime) {
        if (marime > 0) this->marime = marime;
    }
    int getMarime() const { return marime; }
};
int main() {
    Cutie c;
    c.setMarime(4);
    c.setMarime(-2);
    cout << c.getMarime();
}

6. union Valoare { int i; double d; }; Scrii v.i = 1; si apoi v.d = 2.5;. Ce membru poti citi corect acum?

7. Ce afiseaza programul?
#include <iostream>
using namespace std;
class Contor {
    int valoare = 0;
public:
    void creste() { valoare++; }
    int get() const { return valoare; }
};
void mareste(Contor c) { c.creste(); }
int main() {
    Contor a;
    a.creste();
    mareste(a);
    Contor b = a;
    b.creste();
    cout << a.get() << " " << b.get();
}

Scor: 0/7

Codul din repository: S03

Varianta in engleza